Azure App Service — Deploying and Configuring ASP.NET Core
Deploy and configure ASP.NET Core applications on Azure App Service: deployment slots, app settings, connection strings, scaling, managed identity, and production-ready configuration patterns.
Why App Service for Clinical .NET Applications
Azure App Service:
→ PaaS: Microsoft manages the OS, runtime, patching
→ Native .NET 8/9 support — deploy a zip, get a running app
→ Deployment slots: Blue/Green deployments with swap
→ Built-in scaling: manual or automatic
→ Managed Identity: no credentials in connection strings
→ HTTPS termination and custom domains built-in
When App Service is the right choice:
→ 1-50 developer team
→ No need for container orchestration (K8s)
→ Modular monolith or single API
→ Team does not have AKS operational experience
When to move beyond App Service:
→ Need per-module independent scaling (consider AKS)
→ Need Windows AND Linux containers (consider AKS)
→ Deploy more than 10 separate services (App Service becomes expensive)Basic Deployment
# GitHub Actions: build and deploy to Azure App Service
name: Deploy to Azure App Service
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.x'
- name: Build and publish
run: |
dotnet publish src/Host/SystemForge.Api/SystemForge.Api.csproj \
--configuration Release \
--output ./publish
- name: Deploy to Azure App Service
uses: azure/webapps-deploy@v3
with:
app-name: 'clinical-platform-prod'
publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE }}
package: './publish'Configuration — App Settings and Connection Strings
// In Azure Portal or Bicep: set App Settings
// These override appsettings.json at runtime
// Never put secrets in appsettings.json — use Key Vault references
// appsettings.json (checked into source control — no secrets here)
{
"Logging": {
"LogLevel": { "Default": "Information" }
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Clinical": "Server=localhost;Database=ClinicalDev;Trusted_Connection=True;"
// Overridden in Azure by App Service connection string settings
}
}
// In Azure App Service → Configuration → Connection strings:
// Name: Clinical
// Value: Server=clinical-sql.database.windows.net;Database=Clinical;Authentication=Active Directory Managed Identity
// Type: SQLAzure
// In Program.cs — read configuration normally:
builder.Services.AddDbContext<ClinicalDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Clinical")));
// App Service injects the connection string as an environment variable at runtimeManaged Identity — No Credentials in Code
// Instead of: "Server=...;User Id=sa;Password=secret123"
// Use Managed Identity — App Service authenticates to SQL with its Azure AD identity
// Azure SQL: add App Service identity as a database user
// Run in Azure SQL Database:
// CREATE USER [clinical-platform-prod] FROM EXTERNAL PROVIDER;
// ALTER ROLE db_datareader ADD MEMBER [clinical-platform-prod];
// ALTER ROLE db_datawriter ADD MEMBER [clinical-platform-prod];
// Connection string (no credentials):
// "Server=clinical-sql.database.windows.net;Database=Clinical;Authentication=Active Directory Managed Identity"
// Program.cs — no changes needed for basic Managed Identity with SQL Azure
builder.Services.AddDbContext<ClinicalDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Clinical")));
// EF Core + Npgsql/SqlClient pick up Managed Identity automatically from the connection string
// Accessing Key Vault with Managed Identity:
builder.Configuration.AddAzureKeyVault(
new Uri($"https://clinical-kv.vault.azure.net/"),
new DefaultAzureCredential()); // uses Managed Identity in production, dev credentials locally
// Secrets in Key Vault flow into IConfiguration — no code changes neededDeployment Slots (Blue/Green)
App Service deployment slots:
→ Production slot: https://clinical-platform.azurewebsites.net
→ Staging slot: https://clinical-platform-staging.azurewebsites.net
Deployment flow:
1. Deploy new version to staging slot (no downtime on production)
2. Run smoke tests against staging
3. Swap staging → production (near-zero downtime)
4. If issues: swap back (rollback in seconds)
GitHub Actions with staging slot:
- name: Deploy to staging slot
uses: azure/webapps-deploy@v3
with:
app-name: 'clinical-platform'
slot-name: 'staging'
publish-profile: ${{ secrets.AZURE_STAGING_PUBLISH_PROFILE }}
package: './publish'
- name: Swap staging to production
uses: azure/cli@v2
with:
inlineScript: |
az webapp deployment slot swap \
--resource-group clinical-rg \
--name clinical-platform \
--slot staging \
--target-slot productionScaling Configuration
// Bicep: auto-scale App Service based on CPU
resource autoscale 'Microsoft.Insights/autoscalesettings@2022-10-01' = {
name: 'clinical-platform-autoscale'
location: location
properties: {
enabled: true
targetResourceUri: appServicePlan.id
profiles: [
{
name: 'Default'
capacity: { minimum: '2', maximum: '10', default: '2' }
rules: [
{
// Scale out: CPU above 70% for 5 minutes
metricTrigger: {
metricName: 'CpuPercentage'
timeGrain: 'PT1M'
statistic: 'Average'
timeWindow: 'PT5M'
operator: 'GreaterThan'
threshold: 70
}
scaleAction: {
direction: 'Increase'
type: 'ChangeCount'
value: '1'
cooldown: 'PT5M'
}
}
]
}
]
}
}Production issue I've seen: A clinical platform stored the SQL connection string (including username and password) directly in
appsettings.Production.json, which was checked into the repository. Six months later, a developer accidentally pushed this file to a public fork for a demo. The credentials were exposed for 4 hours before the breach was detected. Rotation required emergency downtime across 3 environments. Managed Identity would have meant no credentials to expose — the connection string contains only the server address, no secrets. The migration to Managed Identity took 2 hours. The breach incident took 3 days to fully remediate.
Key Takeaway
App Service is ideal for clinical .NET applications: managed runtime, built-in HTTPS, deployment slots for Blue/Green deployment, and Managed Identity for credential-free Azure resource access. Never store credentials in configuration files or source control — use Managed Identity for SQL, Redis, and Service Bus, and Key Vault for all secrets. Use deployment slots to validate new versions in staging before swapping to production with near-zero downtime.
Found this helpful?
Leave a comment
Have a question, correction, or just found this helpful? Leave a note below.