Skip to content

Security Operations

Protect App Service workloads with layered controls: identity, authentication, transport security, network boundaries, and operational governance. This guide focuses on language-agnostic hardening steps.

Prerequisites

  • Existing Web App and App Service Plan
  • Azure Entra tenant and permissions for identity/auth configuration
  • Security ownership defined for app, platform, and network controls
  • Variables set:
    • RG
    • APP_NAME

When to Use

Procedure

flowchart TD
    A[Security Operations] --> B[Transport]
    A --> C[Identity]
    A --> D[Network]
    A --> E[Application]
    A --> F[Secrets]
    B --> B1[HTTPS-only]
    B --> B2[TLS 1.2+]
    C --> C1[Managed Identity]
    C --> C2[Platform Auth]
    D --> D1[Access Restrictions]
    D --> D2[Private Endpoints]
    E --> E1[CORS]
    E --> E2[Security Headers]
    F --> F1[Key Vault References]
    F --> F2[Secret Rotation]

Security Baseline Checklist

Apply these baseline controls first:

  1. HTTPS-only enabled
  2. Minimum TLS version enforced
  3. Managed identity enabled
  4. Access restrictions configured
  5. Authentication policy chosen (platform and/or app)
  6. Secrets stored outside application code

Enforce HTTPS and TLS Minimum

az webapp update \
  --resource-group $RG \
  --name $APP_NAME \
  --https-only true \
  --output json

az webapp config set \
  --resource-group $RG \
  --name $APP_NAME \
  --min-tls-version 1.2 \
  --output json
Command Description
az webapp update ... Enforces HTTPS-only access on the web app so App Service redirects plain HTTP requests.
az webapp config set ... Sets the site's minimum accepted TLS version to 1.2.

Verify settings:

az webapp show \
  --resource-group $RG \
  --name $APP_NAME \
  --query "{httpsOnly:httpsOnly,state:state}" \
  --output json

az webapp config show \
  --resource-group $RG \
  --name $APP_NAME \
  --query "{minTlsVersion:minTlsVersion,ftpsState:ftpsState}" \
  --output json
Command Description
az webapp show ... Retrieves the app resource so you can confirm HTTPS-only is enabled and the site is running.
az webapp config show ... Retrieves the site configuration so you can confirm the enforced minimum TLS version and current FTPS state.

Enable System-Assigned Managed Identity

az webapp identity assign \
  --resource-group $RG \
  --name $APP_NAME \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the web app.
--name $APP_NAME Enables a system-assigned managed identity on the selected app.
--output json Returns the identity assignment result as JSON.

Retrieve principal ID:

az webapp identity show \
  --resource-group $RG \
  --name $APP_NAME \
  --query "{principalId:principalId,tenantId:tenantId,type:type}" \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the app.
--name $APP_NAME Selects the app whose managed-identity details you want to inspect.
--query "{principalId:principalId,tenantId:tenantId,type:type}" Returns the managed identity object fields you typically need for RBAC work: principal ID, tenant ID, and identity type.
--output json Formats the filtered identity object as JSON.

Sample output (PII-masked):

{
  "principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "tenantId": "<tenant-id>",
  "type": "SystemAssigned"
}

Configure Platform Authentication (App Service Auth)

Enable platform authentication with Entra ID:

az webapp auth update \
  --resource-group $RG \
  --name $APP_NAME \
  --enabled true \
  --action LoginWithAzureActiveDirectory \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the app.
--name $APP_NAME Selects the app where platform authentication should be enabled.
--enabled true Turns on App Service Authentication for the web app.
--action LoginWithAzureActiveDirectory Configures unauthenticated requests to use Microsoft Entra ID sign-in flow.
--output json Returns the updated authentication configuration as JSON.

Alternatively configure provider-specific details:

az webapp auth microsoft update \
  --resource-group $RG \
  --name $APP_NAME \
  --client-id "<app-registration-client-id>" \
  --client-secret "<client-secret>" \
  --allowed-audiences "api://<app-registration-client-id>" \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the app.
--name $APP_NAME Selects the app whose Microsoft identity-provider settings you want to configure.
--client-id "<app-registration-client-id>" Tells App Service which Entra app registration represents this application.
--client-secret "<client-secret>" Supplies the secret App Service uses when it talks to the Entra identity provider on behalf of the app.
--allowed-audiences "api://<app-registration-client-id>" Limits accepted tokens to the specified application audience.
--output json Returns the updated provider-specific authentication settings as JSON.

Protect client secrets

Never store client secrets in source control or plain-text operational notes. Prefer managed identity and secure secret stores whenever possible.

Portal view: Authentication blade (empty state)

Authentication blade for the Web App with a minimal command bar offering Refresh, Troubleshoot, and Send us your feedback actions. The main content area is an empty state with a large silhouette-and-key avatar icon, a heading "Add an identity provider", a subtitle reading "Choose an identity provider to manage the user identities and authentication flow for your application. Providers include Microsoft, Facebook, Google, and Twitter.", a "Learn more about identity providers" link, and a primary blue "Add identity provider" call-to-action button. The left navigation has the Settings group highlighted.

The Authentication blade in its empty state is the visual representation of the gap the az webapp auth update and az webapp auth microsoft update commands above close. When no provider has been configured, the platform performs no token validation at all — every request reaches the application regardless of identity, which is why this empty state should be considered a security finding for any production app that expects authenticated traffic. The Add identity provider button walks through the same provisioning the CLI performs but additionally surfaces app-registration creation, redirect-URI selection, and token-store toggles that are easy to misconfigure when scripted blind. After running the CLI commands above, return to this blade and confirm the empty state has been replaced by a configured provider row — the platform-authentication layer in the defense-in-depth control list further down depends on this Portal surface no longer reading "Add an identity provider".

Restrict Inbound Access by IP or Private Networking

az webapp config access-restriction add \
  --resource-group $RG \
  --name $APP_NAME \
  --rule-name AllowCorp \
  --action Allow \
  --ip-address 203.0.113.0/24 \
  --priority 100 \
  --output json

az webapp config access-restriction add \
  --resource-group $RG \
  --name $APP_NAME \
  --rule-name DenyAll \
  --action Deny \
  --ip-address 0.0.0.0/0 \
  --priority 2147483647 \
  --output json
Command Description
az webapp config access-restriction add ... --rule-name AllowCorp ... Adds an allow rule that permits traffic from the 203.0.113.0/24 corporate CIDR before lower-priority rules are evaluated.
az webapp config access-restriction add ... --rule-name DenyAll ... Adds a lowest-priority catch-all deny rule so any source not matched by earlier allow rules is blocked.

Secure Secrets and Configuration

Recommended controls:

  • Store credentials in secure secret store services
  • Use Key Vault references in app settings where possible
  • Rotate secrets on a fixed schedule
  • Audit secret access and failed retrieval events

Set Key Vault reference style app setting:

az webapp config appsettings set \
  --resource-group $RG \
  --name $APP_NAME \
  --settings "DB_PASSWORD=@Microsoft.KeyVault(SecretUri=https://kv-shared.vault.azure.net/secrets/db-password/)" \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the app.
--name $APP_NAME Selects the app that should read the database password from Key Vault.
--settings "DB_PASSWORD=@Microsoft.KeyVault(...)" Sets the DB_PASSWORD app setting to a Key Vault reference so App Service resolves the secret value at runtime instead of storing the literal secret in the site config.
--output json Returns the updated app settings payload as JSON.

Harden Publishing and Administrative Surfaces

Disable insecure FTP where policy requires:

az webapp config set \
  --resource-group $RG \
  --name $APP_NAME \
  --ftps-state Disabled \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the app.
--name $APP_NAME Selects the app whose publishing surface you want to harden.
--ftps-state Disabled Disables FTP/FTPS publishing access for the site.
--output json Returns the updated site configuration as JSON.

Prefer deployment through secure CI/CD identities and least privilege RBAC.

Configure CORS

Set allowed origins for cross-origin requests:

az webapp cors add \
  --resource-group $RG \
  --name $APP_NAME \
  --allowed-origins "https://frontend.example.com" "https://admin.example.com" \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the app.
--name $APP_NAME Selects the app whose platform CORS policy you want to update.
--allowed-origins "https://frontend.example.com" "https://admin.example.com" Adds exactly these two origins to the platform-managed CORS allowlist.
--output json Returns the updated CORS configuration as JSON.

View current CORS configuration:

az webapp cors show \
  --resource-group $RG \
  --name $APP_NAME \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the app.
--name $APP_NAME Selects the app whose current platform CORS configuration you want to inspect.
--output json Returns the current CORS settings as JSON.

Remove a specific origin:

az webapp cors remove \
  --resource-group $RG \
  --name $APP_NAME \
  --allowed-origins "https://old-frontend.example.com" \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the app.
--name $APP_NAME Selects the app whose platform CORS allowlist you want to prune.
--allowed-origins "https://old-frontend.example.com" Removes this specific origin from the allowed-origin list.
--output json Returns the updated CORS configuration as JSON.

Avoid wildcard origins with credentials

Setting --allowed-origins "*" allows any origin. When combined with App Service Authentication, this can expose tokens to unauthorized frontends. Always specify explicit origins in production.

Platform CORS vs Application CORS

App Service platform CORS and application-level CORS middleware (e.g., Flask-CORS, Express cors) can conflict. Use one or the other, not both. If the platform handles CORS, disable it in your application code to avoid duplicate headers.

Configure Security Headers

App Service does not set security headers by default. Add them via application code or web.config/custom startup.

Recommended production headers:

Header Recommended Value Purpose
Strict-Transport-Security max-age=31536000; includeSubDomains Enforce HTTPS via HSTS
X-Content-Type-Options nosniff Prevent MIME sniffing
X-Frame-Options DENY Prevent clickjacking
Content-Security-Policy default-src 'self' Prevent XSS and injection
Referrer-Policy strict-origin-when-cross-origin Limit referrer leakage
Permissions-Policy camera=(), microphone=(), geolocation=() Restrict browser features

Verify headers are present:

curl --silent --head "https://$APP_NAME.azurewebsites.net" | grep -iE "(strict-transport|x-content-type|x-frame|content-security|referrer-policy|permissions-policy)"

Where to set headers

On Linux App Service, set headers in your application framework (Flask, Express, Spring, ASP.NET middleware). On Windows, you can also use web.config custom headers. For both, Azure Front Door can inject headers at the edge.

Verification

Authentication and identity:

az webapp auth show \
  --resource-group $RG \
  --name $APP_NAME \
  --output json

az webapp identity show \
  --resource-group $RG \
  --name $APP_NAME \
  --output json
Command Description
az webapp auth show ... Retrieves the effective App Service Authentication configuration so you can verify the platform auth policy that is currently active.
az webapp identity show ... Retrieves the current managed identity configuration so you can verify the app still has the expected identity assignment.

Access restrictions:

az webapp config access-restriction show \
  --resource-group $RG \
  --name $APP_NAME \
  --output json
Flag Description
--resource-group $RG Targets the resource group that contains the app.
--name $APP_NAME Selects the app whose inbound access rules you want to inspect.
--output json Returns the current access-restriction configuration as JSON.

Transport checks:

curl --silent --show-error --include "http://$APP_NAME.azurewebsites.net"
curl --silent --show-error --include "https://$APP_NAME.azurewebsites.net"

Expected:

  • HTTP redirects to HTTPS
  • TLS meets minimum baseline
  • unauthorized requests challenged or denied by policy

Rollback / Troubleshooting

Authentication redirect loop

  • verify allowed redirect URIs in app registration
  • ensure hostnames match custom domain configuration
  • confirm authentication policy aligns with reverse proxy setup

Managed identity access denied

  • verify role assignments on target resource
  • confirm principal ID used in role assignment is current
  • allow propagation delay after role changes

Unexpected public access

  • review access restriction priorities
  • confirm deny-all rule exists
  • verify private endpoint DNS resolution path

Advanced Topics

Defense-in-Depth Pattern

Combine:

  • private inbound networking
  • authentication at platform layer
  • authorization in application layer
  • managed identity for outbound resource access
  • central policy enforcement

Security Operations Cadence

Run periodic activities:

  • monthly access review
  • quarterly secret rotation verification
  • recurring incident simulation for auth/network outage
  • security baseline drift report

Policy and Compliance at Scale

Use Azure Policy to enforce controls such as:

  • HTTPS-only required
  • minimum TLS version
  • managed identity required
  • diagnostic settings required

Enterprise Considerations

Security posture improves when baseline configuration is enforced by policy and continuously audited, not only documented. Treat configuration drift as a security incident precursor.

Language-Specific Details

For language-specific security patterns and auth integration:

See Also

Sources