Azure OpenAI is how most enterprises run OpenAI models with Azure's identity, networking, compliance and billing, and in 2026 it lives inside Microsoft Foundry, the platform previously called Azure AI Foundry. This guide is for .NET developers who need to deploy models, call them securely from C#, and operate them in production without surprises. You will learn the Foundry resource model, the recommended SDKs, keyless authentication with managed identity, guardrails, quotas and 429 handling, standard versus provisioned throughput, the Foundry Agent Service, private networking and practical cost controls.
What Is Microsoft Foundry (Formerly Azure AI Foundry)?#
Microsoft Foundry is Azure's unified platform for building AI applications and agents. It brings models, agents and tools together under a single Azure resource with shared role-based access control, networking, policies, tracing, monitoring and evaluation. The product has been renamed several times; the current name, Microsoft Foundry, arrived with Microsoft Ignite in November 2025, so you will still see "Azure AI Foundry" and "Azure AI Studio" in older articles and samples.
The core concepts are straightforward:
- Foundry resource: the Azure resource that owns model deployments, keys, networking and policies. Existing Azure OpenAI resources can be upgraded to Foundry resources while keeping their endpoint, API keys and state.
- Project: a workspace inside a Foundry resource for agents, evaluations, tracing and connections. The current portal is built around Foundry projects; older hub-based projects appear in the Foundry (classic) portal.
- Model catalog: more than 10,000 models from Microsoft, OpenAI, Anthropic, Meta and others. Models sold directly by Azure, including Azure OpenAI models, run as serverless deployments that you call through an endpoint.
- Deployment: a named instance of a model version with a deployment type and capacity. In API calls the deployment name, not the underlying model name, is what you pass as
model. - Foundry Agent Service: a managed runtime for prompt agents, voice agents and hosted agents that run your own code.
How Foundry Works: Endpoints, Deployments and Planes#
Foundry separates the control plane, where you create resources, deployments, networking and role assignments through the portal, Azure CLI, Bicep or Terraform, from the data plane, where applications run inference, agents and evaluations. The distinction matters for security: an identity that can create deployments cannot necessarily call them, and vice versa.
Applications reach the data plane through a few endpoint shapes:
| Endpoint | URL shape | Use it for | .NET SDK |
|---|---|---|---|
| OpenAI v1 | https://<resource>.openai.azure.com/openai/v1/ | Chat completions, Responses, embeddings, images, audio | OpenAI |
| Foundry project | https://<resource>.services.ai.azure.com/api/projects/<project> | Agents, conversations, evaluations, project features | Azure.AI.Projects |
| Anthropic | https://<resource>.services.ai.azure.com/anthropic | Claude models through the Messages API | REST |
The v1 endpoint is the big simplification of the last year. Opt-in availability started in August 2025, and it removes the monthly api-version parameters, accepts the same request shapes as OpenAI, supports automatic Entra ID token refresh in the standard OpenAI client and can also serve models from other providers that support the chat completions syntax. As a result, Microsoft now recommends the plain OpenAI NuGet package for Azure OpenAI. The Azure.AI.OpenAI changelog itself suggests removing that package in favor of the OpenAI SDK, and the older Azure AI Inference SDK has been retired, with an official migration guide to the OpenAI SDK.
Getting Started: Deploy a Model and Call It from .NET#
You can deploy from the Foundry portal, but scripting deployments keeps environments reproducible. The Azure CLI creates a Global Standard deployment of a model in an existing Foundry resource; capacity is expressed in quota units, and for many chat models one unit corresponds to 1,000 tokens per minute:
az cognitiveservices account deployment create \
-n contoso-foundry -g rg-ai-prod \
--deployment-name chat \
--model-name gpt-4o-mini --model-version 2024-07-18 --model-format OpenAI \
--sku-name GlobalStandard --sku-capacity 50
# Grant your developer or app identity data-plane access for inference.
RESOURCE_ID=$(az resource show -g rg-ai-prod -n contoso-foundry \
--resource-type "Microsoft.CognitiveServices/accounts" --query id -o tsv)
az role assignment create --assignee-object-id "$OBJECT_ID" \
--role "Cognitive Services User" --scope "$RESOURCE_ID"Then call the deployment with the OpenAI SDK, the v1 endpoint and Microsoft Entra ID. No API key is involved: BearerTokenPolicy acquires and refreshes tokens for the https://ai.azure.com/.default scope:
#pragma warning disable OPENAI001
using System.ClientModel.Primitives;
using Azure.Identity;
using OpenAI;
using OpenAI.Chat;
string endpoint = "https://contoso-foundry.openai.azure.com/openai/v1/";
ChatClient chat = new(
model: "chat", // the deployment name
authenticationPolicy: new BearerTokenPolicy(
new DefaultAzureCredential(), "https://ai.azure.com/.default"),
options: new OpenAIClientOptions { Endpoint = new Uri(endpoint) });
ChatCompletion completion = await chat.CompleteChatAsync(
new SystemChatMessage("You are a concise assistant for an insurance company."),
new UserChatMessage("Explain what a deductible is in two sentences."));
Console.WriteLine(completion.Content[0].Text);The #pragma acknowledges the OPENAI001 experimental diagnostic that Microsoft's own samples suppress for this authentication path. Role assignments can take several minutes to propagate, so a 401 or 403 immediately after granting a role is usually a timing issue.
Calling Models from .NET: Responses API, Foundry SDK and Microsoft.Extensions.AI#
The same v1 endpoint serves the Responses API, which is the recommended API for new agentic work because it supports reasoning, built-in tools and server-side conversation state. The Responses surface in the OpenAI .NET library is still marked experimental:
#pragma warning disable OPENAI001
using OpenAI.Responses;
ResponsesClient responses = new(
new BearerTokenPolicy(new DefaultAzureCredential(), "https://ai.azure.com/.default"),
new ResponsesClientOptions { Endpoint = new Uri(endpoint) });
ResponseResult result = await responses.CreateResponseAsync(
"chat", "List three risks of storing API keys in source control.");
Console.WriteLine(result.GetOutputText());When you need Foundry-native features, such as agents, conversations, evaluations or project connections, use the project endpoint through Azure.AI.Projects, which reached a stable 2.x release for the current Foundry portal. The project client exposes an OpenAI-compatible Responses client scoped to the project:
#pragma warning disable OPENAI001
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Azure.Identity;
using OpenAI.Responses;
AIProjectClient project = new(
endpoint: new Uri("https://contoso-foundry.services.ai.azure.com/api/projects/support"),
tokenProvider: new DefaultAzureCredential());
ProjectResponsesClient projectResponses = project.ProjectOpenAIClient
.GetProjectResponsesClientForModel("chat");
ResponseResult answer = await projectResponses.CreateResponseAsync(
"What is the size of France in square miles?");
Console.WriteLine(answer.GetOutputText());Note that the project endpoint does not currently route embedding requests, so generate embeddings through the v1 endpoint. In application code, wrap either client in the provider-neutral IChatClient from Microsoft.Extensions.AI so the rest of your code stays portable; the Microsoft.Extensions.AI guide covers the pipeline in depth, and the official OpenAI .NET library guide covers the SDK itself.
Keyless Authentication with DefaultAzureCredential and Managed Identity#
API keys grant full access to a resource, cannot express who is calling, and must be rotated and protected. Foundry's own guidance is to use Microsoft Entra ID for production, which enables managed identities, conditional access, per-principal auditing and least-privilege roles. In practice:
- Locally,
DefaultAzureCredentialpicks up your Visual Studio, Azure CLI or Azure Developer CLI sign-in. - In Azure, App Service, Container Apps, AKS with workload identity and Functions use a managed identity. Prefer a user-assigned identity so the principal survives redeployments and can be granted roles in infrastructure code.
- Roles: grant
Cognitive Services Useror the narrowerCognitive Services OpenAI Userfor inference on the resource, andFoundry User(previously named Azure AI User) when the identity also works with project features such as agents and evaluations. Having control-plane rights such as Owner does not grant data-plane access.
DefaultAzureCredential is convenient but probes several credential sources, which adds latency and can pick up an unexpected identity. In production, either construct ManagedIdentityCredential explicitly or set the AZURE_TOKEN_CREDENTIALS environment variable to restrict the chain; when it is set to ManagedIdentityCredential, the credential skips the probe request. A typical ASP.NET Core registration looks like this:
#pragma warning disable OPENAI001
using System.ClientModel.Primitives;
using Azure.Core;
using Azure.Identity;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
var builder = WebApplication.CreateBuilder(args);
TokenCredential credential = builder.Environment.IsDevelopment()
? new DefaultAzureCredential()
: new ManagedIdentityCredential(
ManagedIdentityId.FromUserAssignedClientId(builder.Configuration["AzureAd:ClientId"]!));
var options = new OpenAIClientOptions
{
Endpoint = new Uri(builder.Configuration["Foundry:OpenAIEndpoint"]!),
};
builder.Services.AddChatClient(new ChatClient(
model: builder.Configuration["Foundry:ChatDeployment"]!,
authenticationPolicy: new BearerTokenPolicy(credential, "https://ai.azure.com/.default"),
options: options).AsIChatClient())
.UseFunctionInvocation()
.UseOpenTelemetry();Store endpoints and deployment names in configuration, not code, so the same build promotes cleanly from development to production.
Guardrails and Content Filters#
Every Azure OpenAI deployment runs behind guardrails, formerly called content filters, built on Azure AI Content Safety classifiers. The default policy for text models flags hate and fairness, violence, sexual and self-harm content at the Medium severity threshold on both prompts and completions, detects user prompt injection attacks (jailbreaks) on prompts, and checks completions for protected text and code. Guardrails are configurable: you can create named guardrails with different thresholds, add blocklists, enable indirect attack detection for documents, and, for agents, inspect tool calls and tool responses (a preview capability). Turning categories off entirely requires an approved request for modified guardrails.
Your code must handle two outcomes. A blocked prompt returns HTTP 400 with the error code content_filter, which surfaces as a ClientResultException. A filtered completion returns normally but with a finish reason of ContentFilter:
using System.ClientModel;
using System.Text.Json;
using OpenAI.Chat;
try
{
ChatCompletion completion = await chat.CompleteChatAsync(messages, cancellationToken: ct);
if (completion.FinishReason == ChatFinishReason.ContentFilter)
{
return Results.Ok(new { answer = "I can't help with that request." });
}
return Results.Ok(new { answer = completion.Content[0].Text });
}
catch (ClientResultException ex) when (ex.Status == 400 && IsContentFilter(ex))
{
logger.LogWarning("Prompt blocked by guardrails for user {UserId}", userId);
return Results.BadRequest(new { error = "Your message was blocked by our content policy." });
}
static bool IsContentFilter(ClientResultException ex)
{
BinaryData? body = ex.GetRawResponse()?.Content;
if (body is null) return false;
using JsonDocument doc = JsonDocument.Parse(body);
return doc.RootElement.TryGetProperty("error", out JsonElement error)
&& error.TryGetProperty("code", out JsonElement code)
&& code.GetString() == "content_filter";
}With the Responses API, Foundry reports guardrail results in a top-level content_filters array, an Azure extension that has no typed property in the SDKs, so read it from the raw response when you need per-category details. Log filter events without logging the offending content itself.
Quotas, Rate Limits and Handling 429 Errors#
Standard deployments are governed by quota measured in tokens per minute (TPM), assigned per subscription, region, model and deployment type. When you create a deployment you allocate part of that quota to it, and a requests-per-minute (RPM) limit is set proportionally. You can split one regional quota across several deployments and resources, and request increases through the quota form in the portal. Viewing quota at subscription scope requires the Cognitive Services Usages Reader role.
When a deployment exceeds its limits, the service returns HTTP 429 with a retry-after-ms header. The OpenAI .NET library already retries 408, 429, 500, 502, 503 and 504 responses up to three additional times with exponential backoff, and the default network timeout is 100 seconds. For production traffic you usually want explicit settings:
using System.ClientModel.Primitives;
using OpenAI;
var options = new OpenAIClientOptions
{
Endpoint = new Uri("https://contoso-foundry.openai.azure.com/openai/v1/"),
RetryPolicy = new ClientRetryPolicy(maxRetries: 5),
NetworkTimeout = TimeSpan.FromSeconds(60), // long enough for streaming first tokens
};Retries help with bursts, not sustained overload. For sustained load, spread traffic across deployments or regions, use Global Standard for its higher default quota, cap MaxOutputTokenCount, apply per-user rate limiting in your API, and consider provisioned throughput for predictable high volume.
Standard vs Provisioned Throughput vs Batch#
Every deployment has a deployment type that determines where data is processed, how you pay and what latency to expect. Microsoft's guidance is to start with Global Standard and move only for a specific reason such as data residency, reserved capacity or offline batch work.
| Deployment type | SKU name | Data processing | Billing | Best for |
|---|---|---|---|---|
| Global Standard | GlobalStandard | Any Azure region | Pay per token | Default choice, newest models, highest quota |
| Data Zone Standard | DataZoneStandard | Within the US, EU or APAC data zone | Pay per token | Data zone compliance |
| Standard | Standard | Within one Azure geography | Pay per token | Geography compliance, low volume |
| Global / Data Zone / Regional Provisioned | GlobalProvisionedManaged, DataZoneProvisionedManaged, ProvisionedManaged | Global, zone or region | Per PTU per hour, or reservations | Predictable high throughput, low latency variance |
| Global / Data Zone Batch | GlobalBatch, DataZoneBatch | Global or zone | 50% less than Global Standard, 24-hour target | Large asynchronous jobs |
| Developer | DeveloperTier | Any region | Pay per token | Evaluating fine-tuned models, no SLA |
Provisioned throughput reserves model processing capacity measured in provisioned throughput units (PTUs). PTU quota is model-independent but regional and per deployment type, and having quota does not guarantee capacity, so confirm capacity before buying an Azure reservation for a one-month or one-year discount. Size PTUs from your request rate, prompt and completion sizes and cache hit rate; cached input tokens do not consume PTU capacity. Two features soften the edges: spillover routes overflow from a saturated provisioned deployment to a standard deployment, either for all requests or per request with the x-ms-spillover-deployment header, and priority processing offers lower-latency pay-per-token processing on Global Standard and US Data Zone Standard deployments without a capacity commitment.
Foundry Agent Service#
Foundry Agent Service is a managed runtime for agents. Prompt agents are defined entirely by configuration, a model, instructions and tools, and Foundry runs them with no code to host. Hosted agents run your own code, for example a Microsoft Agent Framework application, as a container with a managed endpoint, scaling, a dedicated Entra identity and observability. Toolboxes let you curate tools such as web search, file search, code interpreter and MCP servers once and share them across agents. From .NET, the Azure.AI.Projects.Agents package creates prompt agents and the project's Responses client talks to them:
#pragma warning disable OPENAI001
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using OpenAI.Responses;
ProjectsAgentDefinition definition = new DeclarativeAgentDefinition("chat")
{
Instructions = "You answer questions about the employee handbook. Cite section numbers.",
};
ProjectsAgentVersion version = project.AgentAdministrationClient.CreateAgentVersion(
"handbook-agent", options: new(definition));
ProjectConversation conversation = project.ProjectOpenAIClient
.GetProjectConversationsClient().CreateProjectConversation();
ProjectResponsesClient agentClient = project.ProjectOpenAIClient
.GetProjectResponsesClientForAgent(
defaultAgent: "handbook-agent", defaultConversationId: conversation.Id);
Console.WriteLine(agentClient.CreateResponse("How many vacation days do I get?").GetOutputText());Choose prompt agents when configuration is enough and you want the platform to own the runtime. Choose code, with Microsoft Agent Framework, when you need custom orchestration, your own tools and tests, or portability outside Azure; you can still deploy that code as a hosted agent.
Private Networking and Data Protection#
For regulated workloads, lock the resource to your network. Set public network access to Disabled and create a private endpoint; Azure then resolves the resource's host name to a private IP through a privatelink private DNS zone linked to your virtual network, so clients keep using the same endpoint URL. If you run custom or on-premises DNS, forward the privatelink subdomain to Azure DNS. Alternatively, allow selected networks and IP ranges, and grant trusted Azure services access through their managed identities. The Agent Service adds virtual network injection or a managed virtual network for outbound traffic, and hosted agents support network egress controls in preview.
Data residency follows the deployment type: Global types may process prompts in any Azure region, Data Zone types stay within the US, EU or APAC zone, and Standard and Regional Provisioned types stay within the chosen geography, while data at rest stays in the resource's geography. Customer-managed keys, Azure Policy restrictions on deployment types and diagnostic logging round out the governance story.
Cost Optimization Tips#
- Right-size the model. Route simple tasks such as classification and extraction to small models, and reserve large reasoning models for hard problems. Foundry's model router can make that choice per request.
- Cap output. Set a maximum output token count on every call; output tokens cost more than input tokens.
- Exploit prompt caching. Keep system prompts, tool definitions and reference text identical and at the start of the prompt. Prompts need at least 1,024 tokens of shared prefix to be cacheable, and cache reads are discounted on standard deployments.
- Batch offline work. Evaluations, backfills and bulk summarization belong on Global Batch at half the Global Standard price.
- Cache complete answers. For repeated, non-personalized questions, a response cache in your app avoids the call entirely.
- Buy PTUs only for steady load. Provisioned throughput pays off at high, predictable utilization; reservations cut the hourly rate further.
- Watch the meters. Export OpenTelemetry token metrics and set Azure budgets and alerts per resource or project.
Best Practices#
- Use the OpenAI SDK with the v1 endpoint for inference and
Azure.AI.Projectsonly for project features. - Go keyless everywhere. Use managed identity in Azure, least-privilege data-plane roles and no API keys in configuration.
- Name deployments by purpose, not by model. A deployment called
chatcan be upgraded to a new model version without changing application configuration. - Plan for model retirements. Models and versions retire on a published schedule; track them and test replacements before the deadline.
- Handle guardrail outcomes explicitly for both blocked prompts and filtered completions, with user-friendly messages.
- Set retry, timeout and token limits deliberately instead of relying on defaults, and add per-user rate limits in your API.
- Keep regions and deployment types in infrastructure code, together with role assignments and network rules.
Common Pitfalls#
- Using the wrong token scope. The v1 endpoint expects
https://ai.azure.com/.default; older samples usehttps://cognitiveservices.azure.com/.default, which leads to 401 errors after migration. - Passing the model name instead of the deployment name. "Model not found" errors usually mean the
modelvalue does not match a deployment name exactly. - Forgetting the
/openai/v1/suffix. The OpenAI client needs the full base path, including the trailing segment. - Assuming Owner means access. Control-plane roles do not grant inference; assign a data-plane role explicitly.
- Retrying 429s without backoff. Tight retry loops extend throttling; respect
retry-after-msand cap retries. - Choosing Global Standard for regulated data. If prompts must stay in the EU or US, use a Data Zone or regional deployment type.
Azure OpenAI in Microsoft Foundry vs the OpenAI Platform#
| Criterion | Azure OpenAI in Microsoft Foundry | OpenAI platform |
|---|---|---|
| .NET SDK | OpenAI with the /openai/v1/ endpoint | OpenAI |
| Authentication | Entra ID (managed identity) or keys | API keys |
| Networking | Private endpoints, VNet integration | Public endpoint |
| Data residency | Global, data zone or geography | OpenAI's regions and data residency options |
| Capacity options | Standard, provisioned PTUs, batch, priority processing | Usage tiers, batch and other OpenAI offerings |
| Safety | Configurable Foundry guardrails | OpenAI moderation and policies |
| Other models | 10,000+ catalog models, including non-OpenAI | OpenAI models |
| New features | Usually shortly after OpenAI | First |
Frequently Asked Questions#
Is Azure AI Foundry the same as Microsoft Foundry?#
Yes. Azure AI Foundry was renamed Microsoft Foundry in November 2025. Existing resources, endpoints and SDKs continue to work, and Azure OpenAI resources can be upgraded to Foundry resources while keeping their endpoints and keys.
Should I still use the Azure.AI.OpenAI package?#
For new code, no. Use the OpenAI package against the /openai/v1/ endpoint with BearerTokenPolicy for Entra ID. The Azure SDK team's migration guidance recommends removing Azure.AI.OpenAI, whose latest releases are betas that track the OpenAI library, unless you depend on an Azure-specific extension.
Which role does my app need to call a model?#
Assign a data-plane role on the Foundry resource: Cognitive Services User or Cognitive Services OpenAI User for inference, plus Foundry User if the app also uses project features such as agents or evaluations. Assign it to the app's managed identity and allow a few minutes for propagation.
When does provisioned throughput make sense?#
When traffic is high and predictable and you need consistent latency. PTUs are billed per hour whether or not you send requests, so they pay off at high utilization, especially with a reservation. Use spillover to a standard deployment for occasional peaks.
How do I call non-OpenAI models deployed in Foundry from .NET?#
Many catalog models that support the chat completions syntax, such as DeepSeek and Grok models, can be called through the same v1 endpoint with the OpenAI SDK by passing the deployment name as model. Anthropic Claude models use a separate Anthropic endpoint and Messages API.
Summary#
- Microsoft Foundry, formerly Azure AI Foundry, hosts Azure OpenAI and thousands of other models behind one resource with shared security and governance.
- Call models with the
OpenAIpackage and the/openai/v1/endpoint, and useAzure.AI.Projectsfor agents and project features. - Authenticate with managed identity and the
https://ai.azure.com/.defaultscope, and grant explicit data-plane roles. - Handle guardrail blocks, 429 throttling and timeouts deliberately, and configure retries and token limits.
- Start with Global Standard, and move to data zone, provisioned or batch deployments for residency, latency or cost reasons.
- Use Foundry Agent Service for managed agents and Microsoft Agent Framework for code-first agents you can host anywhere.