So far you have been clicking in the portal. An application needs the same thing as code: It sends a text to a model and reads the answer. The smallest program of this kind is a chat client. This lesson shows what it looks like with the Foundry SDK, the way Microsoft Learn shows it for C#. The code is not run against Azure; this is how Microsoft Learn shows it. An answer of the model is therefore not printed anywhere.
For the exam: Python
According to Microsoft, the AI-901 exam assumes knowledge of Python syntax, and most examples on Learn are in Python. Our stack is .NET, which is why this course shows C#. The concepts and steps are the same in both languages. If you want to take the exam, read the Python tab next to the C# tab on the Learn page "Quickstart: Get started with Microsoft Foundry SDK". There is no more than this note here: This is not a Python course.
What you need beforehand
- A deployment (lesson 2.1). At Nordlicht it is called
reise-chat; in the example below this name stands where Learn writesgpt-5-mini. Learn says explicitly that you adjust the model name in the code if the deployment is called something else. - The Foundry User role on the project.
- The project endpoint from the portal. According to Learn its format is
https://<resource-name>.services.ai.azure.com/api/projects/<project-name>. For Nordlicht that meansfoundry-nordlichtandnordlicht-ki. - Four NuGet packages that Learn names for C#:
dotnet add package Azure.AI.Projects
dotnet add package Azure.AI.Projects.Agents
dotnet add package Azure.AI.Extensions.OpenAI
dotnet add package Azure.Identity
- A sign-in with
az login(Azure CLI) before the program runs.
According to Learn, the code uses Azure AI Projects 2.x and is not compatible with 1.x. Anyone who finds older examples has to watch for this. The chat code above imports only three of the four packages; Learn lists all four together, and lesson 2.5 also uses Azure.AI.Projects.Agents.
The code
Learn's C# example for "Chat with a model", with two changes: the project endpoint and Nordlicht's deployment name. Long lines are wrapped at natural places, nothing else is changed. The comments in English are also as they stand at Learn.
using Azure.Identity;
using Azure.AI.Projects;
using Azure.AI.Extensions.OpenAI;
using OpenAI.Responses;
#pragma warning disable OPENAI001
// Format: "https://resource_name.services.ai.azure.com/api/projects/project_name"
var foundryProjectEndpoint =
"https://foundry-nordlicht.services.ai.azure.com/api/projects/nordlicht-ki";
// Create project client to call Foundry API
AIProjectClient projectClient = new(
endpoint: new Uri(foundryProjectEndpoint),
tokenProvider: new DefaultAzureCredential());
// Run a responses API call
ProjectResponsesClient responseClient =
projectClient.ProjectOpenAIClient.GetProjectResponsesClientForModel(
"reise-chat"); // supports all Foundry direct models
ResponseResult response = await responseClient.CreateResponseAsync(
"What is the size of France in square miles?");
string outputText = response.GetOutputText();
if (string.IsNullOrWhiteSpace(outputText))
{
throw new InvalidOperationException("Response output text was empty.");
}
Console.WriteLine(outputText);
The question about France is Learn's sample question. Put in one of your own, such as a made-up question about a hotel.
Line by line
using …: The namespaces of the four packages.Azure.Identitybrings the sign-in,Azure.AI.Projectsthe project client,OpenAI.Responsesthe types of the response.#pragma warning disable OPENAI001: suppresses a compiler warning with this number. Why Learn turns it off is not stated on the page. That is open.foundryProjectEndpoint: the address of the project. At Learn it replaces the placeholderyour_project_endpoint.AIProjectClient: the entry point into the project. It gets the address and atokenProvider, hereDefaultAzureCredential(see below).GetProjectResponsesClientForModel("reise-chat"): returns a client for this deployment. The name is the deployment name, not the model name.CreateResponseAsync(…): a call of the Responses API: text in, answer out.awaitwaits until the answer is there.GetOutputText(): fetches the text of the answer. If it is empty, the program throws an error instead of concealing an empty output.Console.WriteLine: writes the text to the console.
As the result, Learn describes only this: You see an answer generated by the model in the console, and that confirms that endpoint, sign-in and deployment work. Nothing more. What the answer says depends on the model.
Whoever uses one of the instant access models from lesson 2.1 enters the model name there, for example gpt-5-mini. According to Learn: The call stays the same, only the name in the code is that of the model instead of that of the deployment.
Sign-in: Entra ID instead of keys
DefaultAzureCredential from Azure.Identity tries several ways of signing in to Microsoft Entra ID one after another and takes the first one that works. On your machine, after az login, that is the Azure CLI's sign-in. If the same program runs in Azure, it finds the application's managed identity there (see "AZ-900 compact", lesson 2.6). The code stays the same. Learn calls this keyless sign-in.
| Microsoft Entra ID (keyless) | API key | |
|---|---|---|
| What | token after a person or identity signs in | a fixed secret value of the resource |
| Permissions | finely graded through roles such as Foundry User | all or nothing per resource |
| Traceability | per person or identity | hard, no person identifiable |
| Stance at Learn | recommended, especially for operation | only for quick experiments |
In addition Learn states: The Foundry Agent Service (lessons 2.4 and 2.5) supports no API keys, only Entra ID. For Nordlicht the choice is thus simple: Entra ID, the Foundry User role, no key in the source code. How to set up managed identities is a topic of the AI track (T10.1).
For orientation: In the AI track, the IChatClient (T2.1) builds the same idea on a more general interface. The Foundry SDK, by contrast, addresses the project interface directly.
Summary table
| Building block | Job |
|---|---|
| Project endpoint | address of the project |
DefaultAzureCredential |
keyless sign-in, locally and in Azure |
AIProjectClient |
entry point into the project |
GetProjectResponsesClientForModel |
client for a deployment |
CreateResponseAsync |
call of the Responses API |
GetOutputText |
text of the answer |
Exercise 1
Nordlicht's program aborts at the first call. Match each symptom to the most likely cause:
- There is no sign-in at all (no account recognized).
- The sign-in works, but the call is rejected because of missing permission.
- The call reports that the deployment is not found.
- The program does not answer with a text, but throws "Response output text was empty."
Causes: (a) The Foundry User role is missing. (b) The name in the code is not the name of the deployment. (c) az login was not run. (d) The model returned an empty text.
Solution 1
- (c) Learn requires
az loginbefore the start; without the sign-in,DefaultAzureCredentialfinds no account. - (a) Learn names missing credentials or an unset role assignment as the cause of authentication errors.
- (b) The code asks for the deployment name (at Nordlicht
reise-chat). Learn writes: If the deployment is called something else, adjust the name in the code. The exact error message is open; it is not in the guide. - (d) The
IsNullOrWhiteSpacecheck in the example catches exactly that.