Fix Azure .NET Deployment Errors: App Service, SDK & Auth
Why This Is Happening
You've built a .NET app. It runs perfectly on your machine. You push it to Azure , and everything breaks. Sound familiar? I've seen this exact scenario play out on dozens of engagements, and the maddening part is that Azure's error messages almost never tell you which of five different root causes is actually the problem.
Here's what's really going on under the hood. Azure App Service, Azure Functions, and the broader Azure ecosystem are designed to host many .NET runtimes side by side , .NET 6, .NET 7, .NET 8, .NET 9. The platform doesn't automatically detect which version your app needs. If your app targets .NET 8 but the App Service stack is still configured for .NET 6, you'll get a cryptic HTTP 500 or a startup crash before a single line of your code even executes.
Then there's the Azure SDK for .NET authentication layer. Locally, you're probably running with your personal Azure CLI credentials or a developer service principal. On Azure itself, the recommended path is managed identity, but wiring that up correctly requires changes both in your code and in the Azure portal. Miss one step and you'll see AuthenticationFailedException or CredentialUnavailableException with almost no actionable detail about what went wrong.
Connection strings are another common trap. Your local appsettings.json has a connection string pointing to a local SQL Server or a dev database. Azure App Service has its own Application Settings panel where those values need to live, and the format is slightly different depending on whether you're connecting to Azure SQL Database, Azure SQL Managed Instance, or Azure PostgreSQL. Entity Framework Core migration runners behave differently in a cloud host context too, which catches a lot of developers off guard during first deployments.
CORS errors are the fourth category I see constantly. When you publish an ASP.NET Core Web API to Azure App Service and your React or Angular front end tries to call it, the browser blocks cross-origin requests unless CORS is configured at both the app code level and the App Service level. Microsoft's official documentation covers this scenario, Web API with CORS in Azure App Service, but the two-layer configuration requirement is easy to miss.
Finally, there's the Application Insights problem. Telemetry that worked in your local dev environment stops flowing once you deploy because the instrumentation key or connection string isn't set as an App Setting, or the ILogger integration wasn't configured for the production host. You're flying blind in production and don't even know it.
None of these are unsolvable. They just require knowing where to look. Let's work through them systematically. Browse all Microsoft fix guides →
The Quick Fix, Try This First
Before you spend an hour reading logs, do this one check. It resolves roughly 40% of Azure .NET deployment errors I encounter.
Open the Azure Portal, navigate to your App Service, and click Configuration in the left-hand menu. Select the General settings tab. Look at the Stack settings section. You'll see three dropdowns: Stack, Major version, Minor version.
Now open your project file (.csproj) locally and look at the TargetFramework element. If your project says net8.0 and the portal shows .NET 6, that's your problem right there. Change the portal setting to match, click Save, then click Continue when prompted. Azure will restart the app automatically.
While you're in General settings, also check the Platform setting. Most modern .NET apps should be on 64-bit. If it's set to 32-bit and your app depends on any 64-bit native libraries, OpenSSL bindings, certain NuGet packages, it will crash at startup with a load failure that looks totally unrelated to bitness.
After saving, go to Overview and click Browse to test the app. If it loads, you're done. If you still see an HTTP 500 or startup failure, pull up Diagnose and solve problems in the left menu. Under the Diagnostic Tools section, open Application Event Logs. The actual exception with a full stack trace will be there. Copy the first exception message, that's what you'll use to work through the steps below.
ASPNETCORE_ENVIRONMENT as an Application Setting (not a Connection String) in Azure. When it's missing, ASP.NET Core defaults to Production mode, which suppresses the developer exception page and makes errors near-impossible to debug through the browser. Set it to Staging temporarily while you're troubleshooting, then flip it back to Production once everything is working.
I know this sounds basic. But the Azure App Service .NET runtime version does not update itself when you redeploy a newer version of your app. It's a manual configuration, and it's the most common source of Azure .NET app deployment failures I've ever seen.
Here's exactly where to go. In the Azure Portal, open your App Service resource. In the left sidebar under Settings, click Configuration. At the top of the main pane, click the General settings tab. You'll see a box labeled Stack settings with the following fields:
- Stack, should be
.NET - Major version, the major version number (e.g.,
.NET 8) - Minor version, the specific patch (e.g.,
.NET 8 (LTS))
Match these to your TargetFramework in the .csproj. For a project with <TargetFramework>net8.0</TargetFramework>, you want .NET 8 (LTS). If you're on .NET 9 (non-LTS), select that explicitly, it won't show up if your App Service Plan doesn't support it, in which case you may need to scale the plan up.
After saving, Azure will show a banner saying the app will restart. Click Continue. Wait about 60 seconds, then check the app. You can confirm the runtime is loaded correctly by calling the following in the Kudu console (Advanced Tools > Go > CMD):
dotnet --version
If the version shown matches your target framework, the runtime is correctly configured. If you see an older version, double-check that you saved the General settings change and that the app fully restarted.
This one frustrates developers the most because it works perfectly locally and silently fails in production. The Azure Identity client library for .NET uses DefaultAzureCredential, which tries a chain of credential sources in a specific order: environment variables, workload identity, managed identity, Azure CLI, Visual Studio, and a few more. Locally, it picks up your Azure CLI login. On Azure App Service, it's supposed to use managed identity, but only if managed identity is actually enabled and the correct permissions are granted.
Step one: enable managed identity. In the Azure Portal, go to your App Service > Identity (under Settings). On the System assigned tab, flip the Status toggle to On. Click Save. Azure will assign an object ID to your app, copy it, you'll need it in a moment.
Step two: grant that identity the right roles. If your app connects to Azure Key Vault, go to the Key Vault resource > Access policies (or Access control (IAM) if you're using RBAC mode). Add the managed identity with at minimum the Key Vault Secrets User role. For Azure SQL Database, run this in your database:
CREATE USER [your-app-service-name] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [your-app-service-name];
ALTER ROLE db_datawriter ADD MEMBER [your-app-service-name];
Step three: in your .NET code, make sure you're actually using DefaultAzureCredential from the Azure.Identity NuGet package, not a hardcoded connection string or service principal client secret. The ASP.NET and Azure SQL managed identity guide in Microsoft's official documentation covers the exact code pattern. If you see CredentialUnavailableException after this, set the AZURE_CLIENT_ID App Setting to the managed identity's client ID, this helps when you have multiple user-assigned identities and DefaultAzureCredential doesn't know which one to use.
I know this is frustrating, especially when your Entity Framework Core app runs migrations perfectly on your dev machine and then throws SqlException: A connection was successfully established with the server, but then an error occurred during the login process on Azure. Here's why this happens and how to fix it.
Azure App Service handles connection strings in two places: the Connection strings section under Configuration, and the Application settings section. For Entity Framework Core with SQL Azure, use the Connection strings section and set the type to SQLAzure. The name must exactly match what your code calls in GetConnectionString("YourName").
The correct Azure SQL Database connection string format looks like this:
Server=tcp:yourserver.database.windows.net,1433;
Initial Catalog=yourdb;
Encrypt=True;
TrustServerCertificate=False;
Connection Timeout=30;
Authentication=Active Directory Default;
The Authentication=Active Directory Default part is critical if you're using managed identity, without it, the driver falls back to SQL authentication and fails if you haven't set up a SQL login. If you're using a username and password instead (not recommended for production), replace the last line with User ID=youruser;Password=yourpassword;.
For Azure PostgreSQL, the connection string format is different and uses Npgsql. The Microsoft docs cover this under "Use .NET to query Azure PostgreSQL." Make sure you install the Npgsql.EntityFrameworkCore.PostgreSQL package and that SSL mode is set, Azure PostgreSQL requires Ssl Mode=Require in the connection string.
After updating connection strings, restart the App Service from Overview > Restart. Changes to Connection strings require a restart to take effect.
Azure Functions has its own set of .NET-specific pitfalls that catch developers every time. The biggest one right now is the split between the in-process model (your function runs in the same process as the Functions host) and the isolated worker model (your function runs in a separate .NET process). If you write your function for one model and the host expects the other, you'll get a startup failure with almost no useful error message in the portal.
Check your .csproj. In-process functions reference Microsoft.NET.Sdk.Functions. Isolated worker functions reference Microsoft.Azure.Functions.Worker and Microsoft.Azure.Functions.Worker.Sdk. If these are mixed up, or if you're on .NET 8 targeting the in-process model (which is being phased out), you'll have problems.
Go to your Function App in the portal > Configuration > Application settings. Find the setting FUNCTIONS_WORKER_RUNTIME. It should be dotnet-isolated for .NET 8+ isolated model or dotnet for the legacy in-process model. Mismatches between this setting and your package references will cause the function host to fail silently.
Also check FUNCTIONS_EXTENSION_VERSION, this should be ~4 for the v4 runtime, which supports .NET 8 and .NET 9. If it's set to ~3, upgrade it. The v3 runtime only supports .NET 6 in-process. After changing either setting, the Function App will restart automatically.
For local debugging issues with Azure Functions, make sure your local.settings.json has "AzureWebJobsStorage": "UseDevelopmentStorage=true" when using the Azurite emulator, or a real storage account connection string. The Functions host requires a storage account even for HTTP-triggered functions, without it, the host won't start and you'll see Microsoft.WindowsAzure.Storage exceptions.
If your .NET Web API is deployed on Azure App Service and your front end is getting blocked by the browser with errors like Access-Control-Allow-Origin header is missing or CORS policy: No 'Access-Control-Allow-Origin' header is present, there are two independent places you need to configure CORS, and you need both.
Location 1: Inside your ASP.NET Core app. In Program.cs, add a CORS policy before app.Build():
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend", policy =>
{
policy.WithOrigins("https://yourfrontend.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
// After var app = builder.Build():
app.UseCors("AllowFrontend");
Location 2: The Azure App Service CORS panel. In the portal, go to your App Service > API > CORS. You'll see an Allowed Origins list. Add your front-end domain here too. The official Microsoft documentation for "Web API with CORS in Azure App Service" specifically calls out that the platform-level CORS setting and the app-level CORS middleware serve different purposes, the platform setting is for simple scenarios without credentials, while credential-bearing requests (cookies, Authorization headers) require in-code middleware.
One important gotcha: if you add * (wildcard) in the Azure portal CORS panel, it conflicts with AllowCredentials() in your code-level policy. You'll get a runtime exception saying wildcard origins can't be used with credentials. Remove the wildcard from the portal, pin the exact origins both in code and in the portal, and the error goes away.
After updating CORS settings in the portal, click Save. No restart is needed, CORS settings apply immediately. Test with a browser developer tools Network tab, look for the Access-Control-Allow-Origin response header on your preflight OPTIONS request.
Advanced Troubleshooting
If the steps above didn't resolve your Azure .NET deployment issues, it's time to go deeper. These techniques are what I reach for on enterprise and domain-joined environments where the surface-level fixes don't apply.
Reading the Azure App Service Event Logs via Kudu
The Azure portal's log stream is useful but often truncated. For the full picture, use the Kudu diagnostic console. Go to your App Service > Advanced Tools > Go. This opens Kudu in a new tab. From there, navigate to Tools > Diagnostic dump or go to CMD and run:
cd D:\home\LogFiles\Application
dir
Look for stdout log files if you've enabled stdout logging in your web.config. For ASP.NET Core apps, stdout logging is controlled by the stdoutLogEnabled attribute. Set it to true temporarily and re-trigger the error. The full exception with inner exceptions and stack traces will appear in those files. Remember to set it back to false after debugging, stdout logging in production generates large files fast.
Application Insights Not Showing Telemetry
The Microsoft documentation covers Application Insights for both the quickstart setup and for Worker Service applications. The most common reason telemetry stops flowing after deployment: the APPLICATIONINSIGHTS_CONNECTION_STRING App Setting is missing or set to the wrong workspace. Connection strings replaced instrumentation keys as the recommended approach, if your code still uses InstrumentationKey= format, it still works but is considered legacy.
Check this setting in Configuration > Application settings. The value should look like:
InstrumentationKey=xxxxx;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/
Copy it directly from your Application Insights resource under Overview > Connection String. Don't type it manually, one wrong character breaks the whole thing.
Azure Key Vault Integration Issues
The official guide for using Azure Key Vault with ASP.NET Core covers the configuration provider pattern. If your app is supposed to pull secrets from Key Vault at startup but the App Service is crashing before it can even read secrets, check the order of your configuration providers in Program.cs. Key Vault must be added after the base configuration is loaded, and the Key Vault URI must be correct. A common mistake:
// Wrong, reads KV URI from config before config is loaded
builder.Configuration.AddAzureKeyVault(
new Uri(builder.Configuration["KeyVaultUri"]),
new DefaultAzureCredential());
// Right, set the KV URI as an App Setting, not a KV secret
builder.Configuration.AddAzureKeyVault(
new Uri(Environment.GetEnvironmentVariable("KEY_VAULT_URI")!),
new DefaultAzureCredential());
Azure AD / OpenID Connect Sign-In Errors
If you're implementing "Add sign-in with Microsoft to an ASP.NET web app" from the official docs and you're hitting AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application, the fix is straightforward. In the Azure portal, go to Azure Active Directory > App registrations > your app > Authentication. Add your App Service URL (e.g., https://yourapp.azurewebsites.net/signin-oidc) to the Redirect URIs list. Azure AD is strict about exact URL matching, trailing slashes matter, HTTP vs HTTPS matters.
Enterprise and Domain-Joined Scenarios
In corporate environments, outbound traffic from Azure App Service may be blocked by network security groups or Azure Firewall rules. If your .NET app can't reach Azure SQL, Azure Service Bus, or other Azure services, check the App Service > Networking > Outbound traffic section. You may need to configure VNet integration and adjust your NSG rules to allow outbound traffic to the service endpoints.
Prevention & Best Practices
The best Azure .NET troubleshooting session is the one you never have to have. After seeing these problems on dozens of projects, here's what the teams who avoid them consistently do differently.
Pin your runtime version explicitly in the project file. Don't rely on the portal being correct. Set <TargetFramework>net8.0</TargetFramework> and use deployment slots to validate that the target App Service has the matching runtime before you swap to production. Add a startup health check endpoint that returns the .NET runtime version, it takes five minutes to write and saves hours of debugging.
Use managed identities from day one, not service principal secrets. The Microsoft Identity platform documentation is clear on this. Managed identities eliminate the secret rotation problem entirely. You don't have a connection string that expires, a client secret that gets accidentally committed to Git, or a service principal that someone deletes. Wire up DefaultAzureCredential in your code from the start, enable system-assigned managed identity on every App Service and Function App you create, and grant it the minimum roles it needs. This approach also applies cleanly for Azure Service Bus, Azure Blob Storage, and Azure Event Hubs, all services that the official Azure SDK for .NET supports through the same identity model.
Set up Application Insights before you write your first feature. The Application Insights quickstart for .NET is genuinely quick, add the NuGet package, add the connection string App Setting, call builder.Services.AddApplicationInsightsTelemetry(). Once it's there, you have request tracing, dependency tracking, and exception capture automatically. Teams that add it after a production incident are always wishing they'd had it earlier.
Store all secrets in Azure Key Vault, not App Settings. App Settings are convenient but visible to anyone with Contributor access to the subscription. Key Vault with RBAC gives you a proper audit trail, secret versioning, and the ability to rotate without touching your app configuration.
Test your Azure SQL Database connection from Kudu before deploying app code. Open the Kudu console, install sqlcmd if it's not available, and verify the connection string resolves and authenticates before you spend time debugging application-level issues. Network connectivity problems look exactly like authentication problems at the application layer.
- Add a
/healthendpoint usingbuilder.Services.AddHealthChecks()and configure App Service health check probes against it - Enable Always On in App Service General Settings, without it, your app cold-starts and the first request often times out
- Set
WEBSITE_RUN_FROM_PACKAGE=1in Application Settings for immutable, faster deployments with no file-lock issues - Configure deployment slots (staging + production) so you can validate Azure-specific behavior before swapping to live traffic
Frequently Asked Questions
Why does my .NET app work locally but fail on Azure App Service with HTTP 500?
This is almost always one of three things: a .NET runtime version mismatch (your app targets .NET 8 but the App Service is configured for .NET 6), a missing Application Setting that your code reads at startup via IConfiguration, or a connection string that exists in your local appsettings.json but hasn't been added to the App Service Configuration panel. Start by checking General Settings for the runtime version, then compare your appsettings.json keys against your App Service Application Settings, any key that's missing in the cloud will return null and may cause a null reference exception before the app even finishes starting up. Enable the developer exception page temporarily by setting ASPNETCORE_ENVIRONMENT to Development as an App Setting to see the full error.
How do I check which .NET version Azure App Service is actually running?
There are two ways. The quick portal method: go to App Service > Configuration > General settings and check the Stack settings section. The definitive method: open Advanced Tools (Kudu), go to the CMD console, and run dotnet --version. The Kudu console shows you exactly what's installed on the host, which may differ from what the portal claims if the setting was changed but the app hasn't restarted yet. Running dotnet --list-runtimes in Kudu also shows all available runtimes on the host, which is useful if you're trying to decide whether to target an LTS or a current release version.
My Azure Functions app crashes with "Could not load file or assembly", what's causing it?
This error almost always means there's a NuGet package version conflict between what your function project references and what the Functions host provides. The Functions v4 runtime (.NET 8 isolated) ships with certain packages built in, if your project adds the same package at a different version, the binding redirect can fail at runtime. First, check if you're accidentally mixing in-process and isolated-worker package references in your .csproj. Then, run dotnet publish -c Release locally and look at the output folder, if the assembly that's failing to load is missing or is an unexpected version, that's your culprit. For isolated worker model apps, make sure FUNCTIONS_WORKER_RUNTIME is set to dotnet-isolated in the Application Settings.
Why isn't managed identity working for my .NET app on Azure even though I enabled it?
Enabling system-assigned managed identity in the portal is only step one, you also have to grant that identity a role on the target resource. For Azure SQL, you need to create a contained database user as shown in Step 2 above. For Azure Key Vault, the identity needs at minimum the Key Vault Secrets User role assignment. For Azure Storage, it needs Storage Blob Data Contributor. Also confirm your NuGet package is Azure.Identity version 1.10 or later, older versions of DefaultAzureCredential had bugs with App Service managed identity that were fixed in later releases. If you have multiple user-assigned identities, set the AZURE_CLIENT_ID App Setting to explicitly tell DefaultAzureCredential which one to use.
How do I view my Azure .NET app's error logs without Application Insights?
If Application Insights isn't set up, you have two options. The faster one: go to App Service > Log stream in the portal, this shows live stdout and stderr output in real time. The more complete one: go to Diagnose and solve problems > Application Event Logs for structured Windows event log entries from the App Service host. For ASP.NET Core apps hosted under IIS on Windows App Service plans, enable stdout logging in your web.config by setting stdoutLogEnabled="true" and stdoutLogFile="\\?\%home%\LogFiles\stdout", then retrieve the files through Kudu > Files > LogFiles folder. Always disable stdout logging after you're done, it has no automatic rotation and will fill your App Service storage quota.
Entity Framework Core migrations fail when I run them against Azure SQL Database, how do I fix this?
Running dotnet ef database update against Azure SQL requires that your local machine's IP is whitelisted in the Azure SQL Server firewall rules, go to the SQL Server resource (not the database) > Networking > Firewall rules and add your current IP. For automated deployments, the better approach is to call dbContext.Database.Migrate() at app startup inside a scoped service, which runs migrations as part of the Azure-hosted app using managed identity. Just be careful with this pattern in scaled-out scenarios, add a distributed lock or use a deployment migration runner script so multiple instances don't run migrations simultaneously. If you're getting login failures during migration from your local machine, make sure your connection string uses your personal Azure AD account credentials and that you have the db_owner role on the target database.