Security Best Practices for Azure Functions¶
Security for Azure Functions must align to runtime behavior: triggers execute with app identity, settings are loaded at startup, retries can repeat sensitive operations, and scale-out multiplies blast radius if permissions are broad. This guide provides practical controls for identity, secret handling, app settings, and network hardening.
Architecture and operations references
For design background and operational runbooks, see Platform Security, Security Operations, and Configuration Operations.
Why This Matters¶
Default rule: authenticate from Functions to Azure resources using managed identity, not embedded secrets.
Apply managed identity for:
- host and binding storage access where supported,
- Key Vault secret resolution,
- Service Bus, Event Hubs, and Storage SDK clients,
- SQL, Cosmos DB, and other Entra-integrated services.
Why this matters operationally:
- no secret rollover outages from expired connection strings,
- per-resource RBAC scopes reduce breach impact,
- identity usage is auditable through control plane and resource logs.
Connection strings with secrets should be exception-only
If a secret-based connection string is unavoidable, isolate it to the minimum scope and define explicit rotation runbooks with cutover validation.
Recommended Practices¶
App settings vs Key Vault references vs environment values¶
Use this decision model:
- Non-sensitive config (feature flags, endpoint names): app settings.
- Sensitive secrets (API keys, passwords): Key Vault reference in app settings.
- Local development values: local environment files not committed to source.
| Configuration source | Typical value type | Best use case | Security level | Rotation complexity | Operational notes |
|---|---|---|---|---|---|
| App settings (plain value) | Non-sensitive flags, endpoint names, tuning knobs | Runtime behavior toggles and non-secret environment values | Medium (platform protected, but readable by config operators) | Low | Keep in IaC/pipeline variables and review change history |
| App settings + Key Vault reference | Passwords, API keys, certificates, connection secrets | Production secrets consumed by Functions host or code | High (secret stays in Key Vault, app setting stores reference) | Medium | Requires managed identity permission and Key Vault network reachability |
| Local environment values | Developer-only secrets and mocks | Local development and integration testing | Variable (depends on developer workstation controls) | Medium | Keep out of source control and rotate when team membership changes |
| CI/CD secret store variables | Pipeline tokens, deployment-only values | Build/deploy automation where direct Key Vault read is unavailable | Medium-High | Medium | Prefer OIDC/managed identity and minimize long-lived pipeline secrets |
Identity-based connections vs classic secret connections¶
Prefer identity-based setting patterns for supported extensions.
Example pattern for host storage:
AzureWebJobsStorage__accountName=<storage-account-name>AzureWebJobsStorage__credential=managedidentity(or equivalent supported pattern)
Avoid classic secret pattern unless required:
AzureWebJobsStorage=<connection-string-with-key>
Key Vault reference syntax and common errors¶
Reference format:
Common operational failures:
- typo in secret URI or missing trailing slash behavior mismatch,
- function identity lacks Key Vault
getpermission, - vault firewall blocks function outbound path,
- secret version pinned unintentionally and never updated.
Function keys are not complete security¶
Function keys are shared secrets for invocation control, not user identity.
Use auth levels intentionally:
anonymous: only with compensating controls (Easy Auth, APIM JWT, private network).function: shared-secret integration scenarios.admin: runtime operations only, never broad exposure.
| Auth level | Primary use case | Caller identity assurance | Risk profile | Required compensating controls |
|---|---|---|---|---|
anonymous | Public ingress behind centralized auth gateway | None at function endpoint | High | Easy Auth or APIM JWT validation, WAF, strict rate limiting, private backend dependencies |
function | Service integration with key-based invocation | Shared secret only | Medium-High | Key rotation policy, source IP restrictions, monitoring for key leakage, optional gateway token validation |
admin | Host/runtime administrative operations | Shared admin key | Very High | Never expose publicly, restrict to trusted ops network and break-glass workflows |
Why keys alone are insufficient¶
- no per-user identity claims,
- hard to enforce conditional access,
- secret leakage grants direct invocation capability,
- weak audit attribution for caller identity.
Recommended pattern:
- User-facing APIs: enforce identity token validation (platform or gateway).
- Service-to-service: managed identity or OAuth token.
- Keep keys as compatibility fallback, not primary trust boundary.
Identity-based connections and required RBAC¶
For identity-based host and binding access, grant least-privilege roles to Function App identity.
Typical role mappings:
| Target resource | Common minimum role for runtime behavior |
|---|---|
| Storage blobs | Storage Blob Data Contributor |
| Storage queues | Storage Queue Data Contributor |
| Storage tables | Storage Table Data Contributor |
| Key Vault (RBAC model) | Key Vault Secrets User |
| Service Bus receive | Azure Service Bus Data Receiver |
| Service Bus send | Azure Service Bus Data Sender |
| Event Hubs receive | Azure Event Hubs Data Receiver |
RBAC scope discipline
Assign roles at the narrowest scope that works (resource, not subscription). Broad Contributor assignments increase blast radius and hide permission drift.
RBAC model for Functions operations¶
Separate operational personas:
- Deployment principal: deploy code/config, no data-plane access unless required.
- Runtime managed identity: data-plane access to dependencies.
- Operations/on-call: monitoring and incident response permissions.
Practical minimums to review:
- deployment identity should avoid Owner where possible,
- runtime identity should avoid general Contributor,
- monitor role assignment drift on app, storage, and Key Vault scopes.
Network security controls that support identity¶
Identity is necessary but not sufficient. Add network boundaries for sensitive workloads.
Core controls:
- access restrictions for approved source IP/ranges,
- private endpoint for private inbound access,
- VNet integration for private outbound dependencies,
- service tags and NSG controls where applicable.
| Control layer | Primary control | Protects against | Plan support snapshot | Notes for operations |
|---|---|---|---|---|
| Edge ingress | Access restrictions (allowlist IP/ranges) | Unapproved internet source access | Y1, FC1, EP, Dedicated | Revalidate allowlists after corporate egress IP changes |
| Private ingress | Private Endpoint | Public inbound exposure | FC1, EP, Dedicated (and plan/region-dependent scenarios) | Pair with private DNS zone governance |
| Outbound isolation | VNet integration + route controls | Unintended egress paths to public endpoints | FC1, EP, Dedicated (plan/region dependent) | Test dependency DNS resolution and failover routes |
| Service boundary | NSG/service tag controls | Lateral movement between subnets/services | With VNet-integrated plans | Keep rules least-privilege and document exceptions |
| Secret boundary | Key Vault firewall + private access | Secret retrieval from unauthorized networks | All plans via design-specific topology | Validate Function outbound path and managed identity permissions together |
See Platform Security and Platform Networking for design combinations.
CORS done safely¶
CORS only protects browser-origin access behavior; it is not authentication.
Best practices:
- allow only known origins,
- avoid wildcard
*in production, - validate staging and production origin lists independently,
- review CORS when front-end domains change.
| CORS practice | Recommended state | Risk when misconfigured | Validation method |
|---|---|---|---|
| Allowed origins | Explicit allowlist only | Any site can invoke browser requests if wildcard is used | Compare configured origins against approved domain inventory |
| Environment separation | Staging and production lists managed separately | Test domains accidentally allowed in production | Check slot/app settings per environment before release |
| Credentialed requests | Only enable when strictly required | Browser may send cookies/tokens to unintended origins | Security test with browser dev tools and preflight inspection |
| Change management | CORS updates tied to front-end release checklist | Drift between UI domain changes and backend policy | Add CORS review gate in deployment checklist |
Wildcard CORS anti-pattern
* with sensitive browser APIs can expose data to unintended origins, especially when teams assume CORS equals authentication.
TLS and HTTPS enforcement¶
Minimum baseline:
- enforce
HTTPS-onlyon every Function App, - enforce minimum TLS version 1.2 or higher,
- monitor certificate expiry for custom domains,
- verify redirects and TLS settings after infrastructure changes.
| Transport control | Baseline setting | Threat reduced | Audit signal |
|---|---|---|---|
| HTTPS enforcement | HTTPS-only=true | Plaintext transport and downgrade paths | Function App configuration baseline check |
| TLS minimum | min-tls-version=1.2 or higher | Legacy protocol exploitation | Policy compliance and config drift alerts |
| Certificate lifecycle | Track custom domain cert expiry | Service interruption or trust errors | Expiry alert windows (30/14/7 days) |
| Redirect behavior | Validate HTTP to HTTPS redirect path | Users remaining on insecure endpoint due to misrouting | Synthetic probe for HTTP endpoint response |
| Common mistake | Likely impact | Recommended fix | Severity |
|---|---|---|---|
| Treating function keys as sole protection for sensitive API | Unauthorized invocation after key disclosure | Enforce Entra/JWT validation at platform or gateway layer | High |
Broad RBAC assignment (Contributor at subscription) for runtime identity | Expanded blast radius during compromise | Scope roles to resource-level data-plane permissions | High |
| Wildcard CORS in production | Browser-based data exfiltration path | Replace with explicit origin allowlist and periodic review | Medium-High |
| Secret value stored directly in app setting when Key Vault is available | Secret sprawl and harder rotation governance | Replace with Key Vault references and managed identity access | Medium |
| Rotating secret without runtime validation plan | Authentication outage after cutover | Use staged rotation with post-rotation dependency checks | High |
Common Mistakes / Anti-Patterns¶
Secret rotation must be routine, tested, and observable.
Rotation model:
- Create new Key Vault secret version.
- Confirm Function App identity can read it.
- Trigger safe refresh (app restart or configuration refresh workflow as needed).
- Validate dependency connectivity and auth success metrics.
- Revoke old secret version according to retention policy.
| Rotation step | Owner | Key check | Evidence to capture | Rollback trigger |
|---|---|---|---|---|
| Create new secret version | Security/platform engineer | Secret created with expected naming/version metadata | Key Vault secret version ID and timestamp | Version missing metadata or wrong value format |
| Validate identity access | Platform engineer | Function managed identity can get secret | Access test log or successful secret resolution telemetry | Access denied or firewall blocked |
| Refresh runtime resolution | App operator | App restart/refresh completed in planned window | Deployment event + host restart timestamp | Host fails to start or settings not refreshed |
| Execute functional checks | Application owner/on-call | Dependency authentication and critical paths pass | Smoke test output + error-rate dashboard snapshot | Elevated auth failures or dependency timeouts |
| Retire previous secret version | Security/platform engineer | Previous version disabled/revoked per policy | Rotation ticket closure and policy artifact update | Need immediate rollback to prior version |
Operational notes:
- avoid tightly coupled simultaneous rotation for all dependencies,
- rotate high-risk secrets more frequently,
- test rollback path when rotated secret is malformed.
How Functions picks up rotated values
Key Vault references are resolved by platform integration. In production, plan explicit validation after rotation and include restart/refresh procedures in runbooks so update timing is predictable.
flowchart TD
A[Create new secret version in Key Vault] --> B[Validate Function managed identity access]
B --> C{Access check passed}
C -->|No| R1[Fix RBAC or network path]
R1 --> B
C -->|Yes| D[Trigger planned app refresh or restart]
D --> E[Run dependency authentication smoke tests]
E --> F{Validation passed}
F -->|No| G[Rollback to previous secret version]
F -->|Yes| H[Disable old secret version]
H --> I[Close rotation change record] Security boundary and identity flow¶
flowchart LR
Caller[Client or service caller] --> Ingress[Function endpoint\nHTTP trigger]
Ingress --> Auth{Auth control}
Auth -->|"User/API token"| AppAuth[App Service Auth or APIM JWT]
Auth -->|Function key fallback| KeyGate[Shared key gate]
AppAuth --> Code[Function execution]
KeyGate --> Code
Code --> MI[Managed Identity token request]
MI --> KV[Key Vault]
MI --> ST[Storage account]
MI --> SB["Service Bus / Event Hubs"]
subgraph NetworkBoundary[Network boundary]
KV
ST
SB
end Practical hardening commands (long flags only)¶
Enable managed identity:
Set HTTPS-only and TLS minimum:
az functionapp update \
--name "$APP_NAME" \
--resource-group "$RG" \
--https-only true
az functionapp config set \
--name "$APP_NAME" \
--resource-group "$RG" \
--min-tls-version "1.2"
Set Key Vault-backed app setting:
az functionapp config appsettings set \
--name "$APP_NAME" \
--resource-group "$RG" \
--settings "DbPassword=@Microsoft.KeyVault(SecretUri=https://$KEYVAULT_NAME.vault.azure.net/secrets/DbPassword/)"
Grant storage data role to managed identity principal:
az role assignment create \
--assignee-object-id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
--assignee-principal-type "ServicePrincipal" \
--role "Storage Blob Data Contributor" \
--scope "/subscriptions/<subscription-id>/resourceGroups/$RG/providers/Microsoft.Storage/storageAccounts/$STORAGE_NAME"
Validation Checklist¶
- [ ] Managed identity is enabled and used for supported dependencies.
- [ ] Secret-based connection strings are eliminated or formally exception-tracked.
- [ ]
AzureWebJobsStorageuses approved identity-based pattern where supported. - [ ] Key Vault references are used for sensitive app settings.
- [ ] Function keys are not the only control for sensitive HTTP endpoints.
- [ ] RBAC assignments are least-privilege and scoped narrowly.
- [ ] Access restrictions/private endpoints enforce intended ingress boundary.
- [ ] CORS allowlist contains only approved origins; no production wildcard.
- [ ] HTTPS-only and minimum TLS version policy are enforced.
- [ ] Secret rotation runbook is tested and includes validation/rollback steps.