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 writes gpt-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 means foundry-nordlicht and nordlicht-ki.
  • Four NuGet packages that Learn names for C#:
Bash
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.

C#
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.Identity brings the sign-in, Azure.AI.Projects the project client, OpenAI.Responses the 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 placeholder your_project_endpoint.
  • AIProjectClient: the entry point into the project. It gets the address and a tokenProvider, here DefaultAzureCredential (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. await waits 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:

  1. There is no sign-in at all (no account recognized).
  2. The sign-in works, but the call is rejected because of missing permission.
  3. The call reports that the deployment is not found.
  4. 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

  1. (c) Learn requires az login before the start; without the sign-in, DefaultAzureCredential finds no account.
  2. (a) Learn names missing credentials or an unset role assignment as the cause of authentication errors.
  3. (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.
  4. (d) The IsNullOrWhiteSpace check in the example catches exactly that.

Further reading