The platform team standardizes Azure infrastructure on Bicep modules from the public module registry, especially Azure Verified Modules. AVM provides reviewed module contracts for common Azure resource types, so stack authors compose resource groups, identities, storage, networking, and platform services instead of hand-writing every ARM resource block.
This does not remove engineering judgment. Teams still decide naming, RBAC boundaries, tags, diagnostics, network posture, and lifecycle scope. AVM simply moves the low-level resource shape to a maintained baseline and lets the local stack focus on the platform pattern.
The SPA calls out two suffixes that matter: ctl and dat. A control-plane user-assigned managed identity deploys and updates infrastructure. A separate data-plane user-assigned managed identity is granted runtime access to Key Vault, Storage, Event Hubs, databases, and other workload services.
That separation prevents the deployment identity from becoming a universal runtime credential. It also makes reviews easier: control-plane RBAC answers “what can create or modify resources,” while data-plane RBAC answers “what can read or write workload data.”
| Identity | CAF Name | Primary Scope | Typical Permissions |
|---|---|---|---|
| Control-plane UMI | umi-{project}-{env}-{region}-ctl | Resource group or deployment stack | Deploy Bicep, assign managed identity to resources, configure diagnostics |
| Data-plane UMI | umi-{project}-{env}-{region}-dat | Service-specific data plane | Read Key Vault secrets, write blobs, publish Event Hub messages |
| Human access | Time-bound PIM role | Emergency or break-glass only | Minimum permissions, documented follow-up PR required |
GitHub Actions authenticates to Azure with OpenID Connect federation. The job requests a short-lived token, Microsoft Entra ID validates the federated credential for the repository and environment, and azure/login receives an Azure access token. No client secret is stored in GitHub, copied into a runner, or rotated by hand.
The variables in this excerpt are non-secret identifiers: client ID, tenant ID, and subscription ID. Real tenant IDs and subscriptions are intentionally represented here with Microsoft Learn-style placeholders.
Traditional ARM or Bicep deployments apply a template to a scope, but lifecycle ownership can be ambiguous after the deployment finishes. Azure Deployment Stacks add a management layer around that deployment: Azure knows which resources belong to the stack, what should happen when resources disappear from the template, and whether unmanaged changes should be denied.
Deny settings are the sharp edge. They can block deletes or updates outside the stack, which makes production safer when used deliberately. The design goal is not to make emergencies impossible; it is to make out-of-band changes visible, time-bound, and followed by a PR so desired state catches up to reality.
| Capability | Traditional Deployment | Deployment Stack |
|---|---|---|
| Resource ownership | Implicit in deployment history | Explicit stack membership |
| Delete behavior | Resources can remain orphaned | Delete or detach behavior is configured |
| Drift controls | Detected by what-if or external checks | Can combine what-if with deny settings |
| Production guardrails | Usually policy, RBAC, and process | Policy, RBAC, process, plus stack-level deny settings |
The delivery model is trunk-based. Engineers create short-lived feature branches, open pull requests, run validation, and merge through protected gates. The main branch represents production-ready desired state. If a bad change lands, the rollback path is a git revert rather than a hand-built undo script.
This is especially important for infrastructure. Portal edits, local az commands, and workspace clickops all create configuration drift. The governance pattern is simple: exploration can happen in dev, but production writes come from CI/CD using federated identity.
| Stage | Signal | Purpose |
|---|---|---|
| Author | Bicep, parameters, README, and tests changed together | Keep infrastructure, documentation, and validation aligned |
| PR Gate | Build, lint, what-if, and review | Catch syntax, policy, and blast-radius issues before merge |
| Plan | az deployment group what-if | Show create, modify, delete, and no-change effects |
| Apply | az stack group create | Reconcile Azure to the approved desired state |
| Drift Check | Scheduled what-if or stack audit | Find manual change before it becomes an incident |
A stack folder contains the composition root: main.bicep, environment-specific main.{env}.bicepparam files, and optional dashboards or runbooks. The root Bicep file should read like a platform bill of materials. It wires AVM modules together, passes shared tags and naming variables, and exposes only the outputs that downstream workloads actually need.
The pattern keeps local modules rare. If a resource type exists in AVM, use the public registry reference first. If a custom wrapper is necessary, it should encode a platform standard that AVM does not cover, not repackage a single resource for convenience.
| Resource | Prefix | CAF Pattern | Sanitized Example |
|---|---|---|---|
| Resource group | rg- | rg-{project}-{env}-{region}-{instance} | rg-skpapp-prd-usw2-001 |
| Function App | func- | func-{project}-{env}-{region}-{instance} | func-skpapp-prd-usw2-001 |
| Key Vault | kv- | kv-{project}-{env}-{region}-{instance} | kv-skpapp-prd-usw2-001 |
| Managed identity | umi- | umi-{project}-{env}-{region}-{purpose} | umi-skpapp-prd-usw2-ctl |
| Storage account | st | st{project}{env}{region}{instance} | stskpappprdusw2001 |
| Virtual network | vnet- | vnet-{project}-{env}-{region}-{instance} | vnet-skpapp-prd-usw2-001 |
| Address space | n/a | Documented in params | 10.0.0.0/16 |
INFRA-###, tenant IDs use Microsoft documentation placeholders, and network examples use 10.0.0.0/16.