☁️

Azure App Service Guide

Cloud & Azure Intermediate 6 min read 1000 words

Azure App Service Guide for .NET Developers

Introduction

Azure App Service is Microsoft’s Platform-as-a-Service (PaaS) offering for hosting web applications, REST APIs, and mobile backends. This comprehensive guide covers everything from basic deployment to advanced features like deployment slots, autoscaling, and networking.


Table of Contents


App Service Plans

An App Service plan defines the compute resources for your web app. Understanding tiers is crucial for performance and cost optimization.

Service Tiers Comparison

Tier Use Case Features Scaling Price Range
Free (F1) Development/testing 1 GB disk, 60 CPU min/day None Free
Shared (D1) Dev/test, low traffic Custom domains None ~$10/month
Basic (B1-B3) Light production Manual scaling, SSL Manual (3 instances) ~$50-200/month
Standard (S1-S3) Production Autoscale, slots, backups Auto (10 instances) ~$70-280/month
Premium (P1v3-P3v3) High-performance VNet, more slots, higher limits Auto (30 instances) ~$140-560/month
Isolated (I1v2-I3v2) Enterprise/compliance Dedicated network, highest scale Auto (100 instances) ~$300-1200/month

Choosing the Right Plan

What are your requirements?
β”œβ”€β”€ Development/testing only?
β”‚   └── Free or Basic tier
β”œβ”€β”€ Production with consistent traffic?
β”‚   └── Standard tier
β”œβ”€β”€ High traffic or need VNet integration?
β”‚   └── Premium tier
β”œβ”€β”€ Compliance requirements (HIPAA, PCI)?
β”‚   └── Isolated tier (App Service Environment)
└── Container-based deployment?
    └── Premium or Isolated tier (Linux)

Creating an App Service Plan

# Azure CLI
az appservice plan create \
  --name my-app-plan \
  --resource-group my-rg \
  --sku P1v3 \
  --is-linux \
  --location eastus

# Create web app in the plan
az webapp create \
  --name my-web-app \
  --resource-group my-rg \
  --plan my-app-plan \
  --runtime "DOTNET|8.0"
// Using Azure SDK for .NET
public async Task CreateAppServiceAsync()
{
    var armClient = new ArmClient(new DefaultAzureCredential());
    var subscription = await armClient.GetDefaultSubscriptionAsync();
    var resourceGroup = await subscription
        .GetResourceGroups()
        .GetAsync("my-rg");

    // Create App Service Plan
    var planData = new AppServicePlanData(AzureLocation.EastUS)
    {
        Sku = new AppServiceSkuDescription
        {
            Name = "P1v3",
            Tier = "PremiumV3",
            Capacity = 1
        },
        Kind = "linux",
        IsReserved = true // Required for Linux
    };

    var plan = await resourceGroup.Value
        .GetAppServicePlans()
        .CreateOrUpdateAsync(WaitUntil.Completed, "my-app-plan", planData);

    // Create Web App
    var webAppData = new WebSiteData(AzureLocation.EastUS)
    {
        AppServicePlanId = plan.Value.Id,
        SiteConfig = new SiteConfigProperties
        {
            LinuxFxVersion = "DOTNETCORE|8.0",
            AlwaysOn = true
        }
    };

    await resourceGroup.Value
        .GetWebSites()
        .CreateOrUpdateAsync(WaitUntil.Completed, "my-web-app", webAppData);
}

Deployment Options

1. ZIP Deploy (Recommended for CI/CD)

# Build and publish
dotnet publish -c Release -o ./publish

# ZIP the output
cd publish && zip -r ../app.zip .

# Deploy using Azure CLI
az webapp deployment source config-zip \
  --resource-group my-rg \
  --name my-web-app \
  --src app.zip

2. Git Deploy

# Configure local git deployment
az webapp deployment source config-local-git \
  --name my-web-app \
  --resource-group my-rg

# Get deployment URL
az webapp deployment list-publishing-credentials \
  --name my-web-app \
  --resource-group my-rg \
  --query scmUri

# Add Azure as remote and push
git remote add azure https://<user>@my-web-app.scm.azurewebsites.net/my-web-app.git
git push azure main

3. GitHub Actions

# .github/workflows/azure-webapp.yml
name: Deploy to Azure App Service

on:
  push:
    branches: [main]

env:
  AZURE_WEBAPP_NAME: my-web-app
  DOTNET_VERSION: '8.0.x'

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Build and Publish
        run: |
          dotnet restore
          dotnet build --configuration Release
          dotnet publish -c Release -o ./publish

      - name: Deploy to Azure
        uses: azure/webapps-deploy@v3
        with:
          app-name: ${{ env.AZURE_WEBAPP_NAME }}
          publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
          package: ./publish

4. Azure DevOps Pipeline

# azure-pipelines.yml
trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  buildConfiguration: 'Release'
  azureSubscription: 'MyAzureSubscription'
  webAppName: 'my-web-app'

stages:
  - stage: Build
    jobs:
      - job: BuildJob
        steps:
          - task: UseDotNet@2
            inputs:
              version: '8.0.x'

          - task: DotNetCoreCLI@2
            inputs:
              command: 'publish'
              publishWebProjects: true
              arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'

          - task: PublishBuildArtifacts@1
            inputs:
              PathtoPublish: '$(Build.ArtifactStagingDirectory)'
              ArtifactName: 'drop'

  - stage: Deploy
    dependsOn: Build
    jobs:
      - deployment: DeployJob
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - task: AzureWebApp@1
                  inputs:
                    azureSubscription: '$(azureSubscription)'
                    appName: '$(webAppName)'
                    package: '$(Pipeline.Workspace)/drop/**/*.zip'

Deployment Slots and Blue-Green Deployments

Deployment slots allow you to run multiple versions of your app simultaneously. This enables zero-downtime deployments and easy rollbacks.

Understanding Slots

App Service: my-web-app
β”œβ”€β”€ Production Slot (my-web-app.azurewebsites.net)
β”‚   └── Currently serving users
β”œβ”€β”€ Staging Slot (my-web-app-staging.azurewebsites.net)
β”‚   └── Deploy new version here first
└── Testing Slot (my-web-app-testing.azurewebsites.net)
    └── For QA testing

Creating Deployment Slots

# Create staging slot
az webapp deployment slot create \
  --name my-web-app \
  --resource-group my-rg \
  --slot staging \
  --configuration-source my-web-app

# Deploy to staging
az webapp deployment source config-zip \
  --name my-web-app \
  --resource-group my-rg \
  --slot staging \
  --src app.zip

Blue-Green Deployment Strategy

Step 1: Deploy to staging (green)
        Production (blue) β†’ Serving traffic
        Staging (green)   β†’ New version deployed

Step 2: Test staging slot
        Access: my-web-app-staging.azurewebsites.net
        Run smoke tests, health checks

Step 3: Swap slots
        Production (green) β†’ Now serving new version
        Staging (blue)     β†’ Contains old version (rollback target)

Step 4: If issues found, swap back immediately

Implementing Blue-Green Deployment

# 1. Deploy to staging slot
az webapp deployment source config-zip \
  --name my-web-app \
  --resource-group my-rg \
  --slot staging \
  --src app.zip

# 2. Warm up staging (optional but recommended)
curl -I https://my-web-app-staging.azurewebsites.net/health

# 3. Run smoke tests against staging
./run-smoke-tests.sh https://my-web-app-staging.azurewebsites.net

# 4. Swap staging to production (zero-downtime)
az webapp deployment slot swap \
  --name my-web-app \
  --resource-group my-rg \
  --slot staging \
  --target-slot production

# 5. If issues found, swap back immediately
az webapp deployment slot swap \
  --name my-web-app \
  --resource-group my-rg \
  --slot production \
  --target-slot staging

Slot-Specific Settings (Sticky Settings)

# Mark settings as slot-specific (won't swap)
az webapp config appsettings set \
  --name my-web-app \
  --resource-group my-rg \
  --slot-settings "SLOT_NAME=production" "ENVIRONMENT=prod"

# For staging slot
az webapp config appsettings set \
  --name my-web-app \
  --resource-group my-rg \
  --slot staging \
  --slot-settings "SLOT_NAME=staging" "ENVIRONMENT=staging"

What Gets Swapped vs. What Doesn’t

Swapped (Default) Not Swapped (Sticky)
General settings (framework version, etc.) Publishing endpoints
App settings (unless marked sticky) Custom domain names
Connection strings (unless marked sticky) Certificates and TLS/SSL settings
Handler mappings Scale settings
Public certificates WebJobs schedulers
Virtual applications and directories IP restrictions
Managed identities Hybrid connections
Diagnostic settings

Configuration Management

Application Settings

// Configuration in ASP.NET Core
var builder = WebApplication.CreateBuilder(args);

// Azure App Service settings automatically loaded
// They appear as environment variables

// Access configuration
var connectionString = builder.Configuration.GetConnectionString("Database");
var apiKey = builder.Configuration["ExternalApi:ApiKey"];

// Strongly-typed configuration
builder.Services.Configure<ExternalApiOptions>(
    builder.Configuration.GetSection("ExternalApi"));
# Set app settings via CLI
az webapp config appsettings set \
  --name my-web-app \
  --resource-group my-rg \
  --settings \
    "ConnectionStrings__Database=Server=..." \
    "ExternalApi__ApiKey=secret" \
    "ASPNETCORE_ENVIRONMENT=Production"

Connection Strings

# Set connection strings (separate from app settings)
az webapp config connection-string set \
  --name my-web-app \
  --resource-group my-rg \
  --connection-string-type SQLAzure \
  --settings Database="Server=tcp:myserver.database.windows.net;..."

Key Vault Integration

// Program.cs - Load secrets from Key Vault
var builder = WebApplication.CreateBuilder(args);

// Add Key Vault configuration provider
if (builder.Environment.IsProduction())
{
    var keyVaultEndpoint = builder.Configuration["KeyVaultEndpoint"];
    builder.Configuration.AddAzureKeyVault(
        new Uri(keyVaultEndpoint),
        new DefaultAzureCredential());
}

// Secrets are now available like regular configuration
var secret = builder.Configuration["MySecret"];
# Enable managed identity
az webapp identity assign \
  --name my-web-app \
  --resource-group my-rg

# Grant Key Vault access
az keyvault set-policy \
  --name my-keyvault \
  --object-id <managed-identity-principal-id> \
  --secret-permissions get list

# Reference Key Vault secrets in app settings
az webapp config appsettings set \
  --name my-web-app \
  --resource-group my-rg \
  --settings "MySecret=@Microsoft.KeyVault(SecretUri=https://my-keyvault.vault.azure.net/secrets/MySecret/)"

Custom Domains and SSL/TLS

Adding Custom Domain

# 1. Add CNAME/A record in DNS
# CNAME: www.example.com β†’ my-web-app.azurewebsites.net
# A record: example.com β†’ <App Service IP>
# TXT record: asuid.www.example.com β†’ <Custom Domain Verification ID>

# 2. Add custom domain to App Service
az webapp config hostname add \
  --webapp-name my-web-app \
  --resource-group my-rg \
  --hostname www.example.com

SSL/TLS Certificates

# Option 1: Free managed certificate (auto-renewed)
az webapp config ssl create \
  --name my-web-app \
  --resource-group my-rg \
  --hostname www.example.com

# Option 2: Import certificate from Key Vault
az webapp config ssl import \
  --name my-web-app \
  --resource-group my-rg \
  --key-vault my-keyvault \
  --key-vault-certificate-name my-cert

# Option 3: Upload PFX certificate
az webapp config ssl upload \
  --name my-web-app \
  --resource-group my-rg \
  --certificate-file mycert.pfx \
  --certificate-password "password"

# Bind certificate to hostname
az webapp config ssl bind \
  --name my-web-app \
  --resource-group my-rg \
  --certificate-thumbprint <thumbprint> \
  --ssl-type SNI

Enforcing HTTPS

// Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// Force HTTPS redirect
if (app.Environment.IsProduction())
{
    app.UseHttpsRedirection();
    app.UseHsts();
}
# Enforce HTTPS at platform level
az webapp update \
  --name my-web-app \
  --resource-group my-rg \
  --https-only true

# Set minimum TLS version
az webapp config set \
  --name my-web-app \
  --resource-group my-rg \
  --min-tls-version 1.2

Authentication and Authorization

Azure App Service provides built-in authentication (β€œEasy Auth”) without code changes.

Enable Easy Auth

# Enable authentication with Entra ID
az webapp auth update \
  --name my-web-app \
  --resource-group my-rg \
  --enabled true \
  --action LoginWithAzureActiveDirectory \
  --aad-client-id <app-registration-client-id> \
  --aad-client-secret <client-secret> \
  --aad-token-issuer-url https://login.microsoftonline.com/<tenant-id>/v2.0

Accessing User Claims in Code

// Easy Auth adds headers with user info
public class UserController : Controller
{
    [HttpGet("me")]
    public IActionResult GetCurrentUser()
    {
        // Easy Auth headers
        var userId = Request.Headers["X-MS-CLIENT-PRINCIPAL-ID"].FirstOrDefault();
        var userName = Request.Headers["X-MS-CLIENT-PRINCIPAL-NAME"].FirstOrDefault();

        // Or parse the full principal
        var principalHeader = Request.Headers["X-MS-CLIENT-PRINCIPAL"].FirstOrDefault();
        if (!string.IsNullOrEmpty(principalHeader))
        {
            var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(principalHeader));
            var principal = JsonSerializer.Deserialize<ClientPrincipal>(decoded);
        }

        return Ok(new { userId, userName });
    }
}

public class ClientPrincipal
{
    public string IdentityProvider { get; set; }
    public string UserId { get; set; }
    public string UserDetails { get; set; }
    public IEnumerable<UserClaim> UserClaims { get; set; }
}

public class UserClaim
{
    public string Type { get; set; }
    public string Value { get; set; }
}

Scaling Strategies

Manual Scaling

# Scale up (vertical) - more powerful instance
az appservice plan update \
  --name my-app-plan \
  --resource-group my-rg \
  --sku P2v3

# Scale out (horizontal) - more instances
az appservice plan update \
  --name my-app-plan \
  --resource-group my-rg \
  --number-of-workers 3

Autoscale Configuration

# Enable autoscale based on CPU
az monitor autoscale create \
  --resource-group my-rg \
  --resource my-app-plan \
  --resource-type Microsoft.Web/serverfarms \
  --name my-autoscale-setting \
  --min-count 2 \
  --max-count 10 \
  --count 2

# Add scale-out rule (CPU > 70%)
az monitor autoscale rule create \
  --resource-group my-rg \
  --autoscale-name my-autoscale-setting \
  --condition "CpuPercentage > 70 avg 5m" \
  --scale out 2

# Add scale-in rule (CPU < 30%)
az monitor autoscale rule create \
  --resource-group my-rg \
  --autoscale-name my-autoscale-setting \
  --condition "CpuPercentage < 30 avg 5m" \
  --scale in 1 \
  --cooldown 10

Autoscale Best Practices

// 1. Disable ARR Affinity for better load distribution
// (Session affinity prevents effective scaling)
az webapp config set \
  --name my-web-app \
  --resource-group my-rg \
  --generic-configurations '{"clientAffinityEnabled": false}'

// 2. Always use scale-out AND scale-in rules together
// Otherwise you'll hit max/min limits and get stuck

// 3. Use appropriate cool-down periods
// Minimum 5-10 minutes to let metrics stabilize

// 4. Set appropriate thresholds with margin
// Scale out at 70%, scale in at 30% (not 70% for both!)

// 5. Consider using multiple metrics
// CPU + Memory + Request Count for more accurate scaling

Automatic Scaling (Premium Plans)

# Enable automatic scaling (simpler than autoscale)
az webapp update \
  --name my-web-app \
  --resource-group my-rg \
  --set siteConfig.autoHealEnabled=true

# Set maximum burst (max instances)
az resource update \
  --resource-group my-rg \
  --name my-web-app \
  --resource-type "Microsoft.Web/sites" \
  --set properties.siteConfig.functionAppScaleLimit=10

Networking

VNet Integration

# Create VNet and subnet
az network vnet create \
  --name my-vnet \
  --resource-group my-rg \
  --address-prefix 10.0.0.0/16 \
  --subnet-name web-subnet \
  --subnet-prefix 10.0.1.0/24

# Delegate subnet to App Service
az network vnet subnet update \
  --name web-subnet \
  --vnet-name my-vnet \
  --resource-group my-rg \
  --delegations Microsoft.Web/serverFarms

# Enable VNet integration
az webapp vnet-integration add \
  --name my-web-app \
  --resource-group my-rg \
  --vnet my-vnet \
  --subnet web-subnet

Private Endpoints

# Create private endpoint for App Service
az network private-endpoint create \
  --name my-private-endpoint \
  --resource-group my-rg \
  --vnet-name my-vnet \
  --subnet private-endpoints-subnet \
  --private-connection-resource-id <app-service-resource-id> \
  --group-ids sites \
  --connection-name my-private-connection

# Create private DNS zone
az network private-dns zone create \
  --name privatelink.azurewebsites.net \
  --resource-group my-rg

# Link DNS zone to VNet
az network private-dns link vnet create \
  --name my-dns-link \
  --resource-group my-rg \
  --zone-name privatelink.azurewebsites.net \
  --virtual-network my-vnet \
  --registration-enabled false

Access Restrictions (IP Filtering)

# Allow specific IP range
az webapp config access-restriction add \
  --name my-web-app \
  --resource-group my-rg \
  --rule-name "AllowOffice" \
  --action Allow \
  --ip-address 203.0.113.0/24 \
  --priority 100

# Allow from specific VNet
az webapp config access-restriction add \
  --name my-web-app \
  --resource-group my-rg \
  --rule-name "AllowVNet" \
  --action Allow \
  --vnet-name my-vnet \
  --subnet web-subnet \
  --priority 200

# Deny all other traffic (implicit)
az webapp config access-restriction set \
  --name my-web-app \
  --resource-group my-rg \
  --default-action Deny

Logging and Diagnostics

Enable Logging

# Enable application logging
az webapp log config \
  --name my-web-app \
  --resource-group my-rg \
  --application-logging filesystem \
  --level verbose \
  --detailed-error-messages true \
  --failed-request-tracing true

# Stream logs in real-time
az webapp log tail \
  --name my-web-app \
  --resource-group my-rg

Application Insights Integration

// Program.cs
builder.Services.AddApplicationInsightsTelemetry(options =>
{
    options.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"];
});

// Custom telemetry
public class OrderService
{
    private readonly TelemetryClient _telemetry;

    public async Task ProcessOrderAsync(Order order)
    {
        using var operation = _telemetry.StartOperation<RequestTelemetry>("ProcessOrder");

        _telemetry.TrackEvent("OrderReceived", new Dictionary<string, string>
        {
            ["OrderId"] = order.Id.ToString(),
            ["CustomerId"] = order.CustomerId
        });

        try
        {
            await DoProcessingAsync(order);
            operation.Telemetry.Success = true;
        }
        catch (Exception ex)
        {
            _telemetry.TrackException(ex);
            operation.Telemetry.Success = false;
            throw;
        }
    }
}
# Enable Application Insights
az webapp config appsettings set \
  --name my-web-app \
  --resource-group my-rg \
  --settings "APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=..."

Health Checks

// Program.cs
builder.Services.AddHealthChecks()
    .AddSqlServer(
        connectionString: builder.Configuration.GetConnectionString("Database"),
        name: "database")
    .AddRedis(
        redisConnectionString: builder.Configuration.GetConnectionString("Redis"),
        name: "redis")
    .AddUrlGroup(
        new Uri("https://external-api.com/health"),
        name: "external-api");

var app = builder.Build();

app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
# Configure health check in App Service
az webapp config set \
  --name my-web-app \
  --resource-group my-rg \
  --generic-configurations '{"healthCheckPath": "/health"}'

Performance Optimization

Always On

# Enable Always On (prevents cold starts)
az webapp config set \
  --name my-web-app \
  --resource-group my-rg \
  --always-on true

Local Cache

# Enable local cache for faster file access
az webapp config appsettings set \
  --name my-web-app \
  --resource-group my-rg \
  --settings "WEBSITE_LOCAL_CACHE_OPTION=Always" \
             "WEBSITE_LOCAL_CACHE_SIZEINMB=1000"

Compression

// Program.cs - Enable response compression
builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
});

var app = builder.Build();
app.UseResponseCompression();

Caching Headers

// Set cache headers for static content
app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        ctx.Context.Response.Headers.Append(
            "Cache-Control", "public,max-age=31536000");
    }
});

Container Deployments

Deploy Docker Container

# Using Azure Container Registry
az webapp create \
  --name my-container-app \
  --resource-group my-rg \
  --plan my-app-plan \
  --deployment-container-image-name myregistry.azurecr.io/myapp:latest

# Configure registry credentials
az webapp config container set \
  --name my-container-app \
  --resource-group my-rg \
  --docker-registry-server-url https://myregistry.azurecr.io \
  --docker-registry-server-user myregistry \
  --docker-registry-server-password <password>

# Or use managed identity (recommended)
az webapp config set \
  --name my-container-app \
  --resource-group my-rg \
  --generic-configurations '{"acrUseManagedIdentityCreds": true}'

Multi-Container (Docker Compose)

# docker-compose.yml
version: '3'
services:
  web:
    image: myregistry.azurecr.io/web:latest
    ports:
      - "80:80"
    environment:
      - REDIS_HOST=redis
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
# Deploy multi-container app
az webapp config container set \
  --name my-multi-app \
  --resource-group my-rg \
  --multicontainer-config-type compose \
  --multicontainer-config-file docker-compose.yml

Cost Optimization

Cost-Saving Strategies

Strategy Savings When to Use
Dev/Test pricing ~50% Non-production environments
Reserved Instances ~40% Predictable production workloads
Autoscale Variable Variable traffic patterns
Stop dev environments 100% when stopped After-hours, weekends
Right-size plans Variable Over-provisioned resources

Auto-Shutdown for Dev Environments

# Using Azure Automation or Logic Apps
# Schedule: Stop at 7 PM, Start at 7 AM

# Stop command
az webapp stop --name dev-app --resource-group dev-rg

# Start command
az webapp start --name dev-app --resource-group dev-rg

Shared Plans for Dev/Test

# Multiple dev apps can share one plan
az webapp create --name dev-app-1 --plan shared-dev-plan --resource-group dev-rg
az webapp create --name dev-app-2 --plan shared-dev-plan --resource-group dev-rg
az webapp create --name dev-app-3 --plan shared-dev-plan --resource-group dev-rg

Interview Questions

1. What is the difference between scaling up and scaling out?

Answer:

  • Scaling up (vertical): Moving to a more powerful instance (e.g., S1 β†’ S3). Increases CPU, memory per instance. Simple but has limits.
  • Scaling out (horizontal): Adding more instances. Better for handling concurrent requests. Requires stateless app design. Preferred for production workloads.

Best practice: Design for scale-out (stateless apps), then scale up if individual requests need more resources.


2. How do deployment slots enable zero-downtime deployments?

Answer: Deployment slots provide:

  1. Separate environment for testing new code
  2. Warm-up before traffic routing
  3. Instant traffic switch (slot swap)
  4. Instant rollback capability

The swap operation:

  • Applies source slot’s settings to target slot instances
  • Waits for instances to restart with new settings
  • Swaps virtual IPs (traffic routing)
  • No requests are dropped during the swap

3. When would you use VNet Integration vs Private Endpoints?

Answer:

  • VNet Integration: Allows App Service to access resources inside a VNet (outbound). Use when your app needs to connect to VMs, databases, or services in a private network.

  • Private Endpoints: Makes App Service accessible only via private IP (inbound). Use when you want to restrict access to your app from the internet.

Typical architecture: Both together - VNet integration for app to access backend resources, private endpoint to restrict who can access the app.


4. What happens during an App Service slot swap?

Answer:

  1. Source slot settings (connection strings, app settings) applied to target slot workers
  2. Target slot workers restart with new configuration
  3. Warm-up requests sent to target slot (using the source slot’s code)
  4. Once warm-up completes, virtual IPs are swapped
  5. Source slot now has target’s previous code (rollback point)

Key points:

  • Settings marked β€œslot-specific” don’t swap
  • Swap can be configured with traffic routing for gradual rollout
  • Auto-swap can be enabled for staging slots

5. How do you troubleshoot a slow App Service application?

Answer:

  1. Check Application Insights: Look at request duration, dependencies, exceptions
  2. Review metrics: CPU, memory, HTTP queue length, response times
  3. Enable profiler: Application Insights profiler for code-level analysis
  4. Check dependencies: Database, external APIs, Redis - are they slow?
  5. Review scaling: Is the app scaled appropriately for load?
  6. Check for cold starts: Is Always On enabled?
  7. Network issues: Check VNet integration, private endpoints
  8. Code issues: Sync over async, N+1 queries, missing caching

Key Takeaways

  1. Choose the right tier - Basic for dev, Standard+ for production
  2. Use deployment slots - Zero-downtime deployments and easy rollbacks
  3. Enable autoscaling - Always set scale-in rules alongside scale-out
  4. Use managed identity - No credentials to manage
  5. Enable Application Insights - Essential for production monitoring
  6. Consider networking - VNet integration for backend access, private endpoints for secure access
  7. Enable Always On - Prevents cold starts in production
  8. Use slot-specific settings - Connection strings, environment variables per slot

Further Reading

πŸ“š Related Articles