Infrastructure as Code: Review the Plan Before Applying It
Choose provisioning and configuration tools, read a Bicep deployment, and understand what validation and what-if do not prove.
A building plan is not the furniture arrangement
Infrastructure provisioning creates the building: networks, storage, compute, identities, and services. Configuration management sets up what operates inside or alongside it: packages, application settings, policies, and desired runtime state.
The boundary can overlap. What matters is clear ownership, repeatability, drift handling, and safe change review, not forcing every setting into one tool.
| Approach | Useful fit | Operational responsibility |
|---|---|---|
| Bicep/ARM | Azure-native declarative provisioning | Scope, API behavior, deployment mode, RBAC |
| Terraform | Provider-based provisioning across environments | State protection, locking, provider/version control |
| Configuration tool | OS/application desired state | Credentials, convergence, drift, target connectivity |
| Immutable image/container | Replace a known runtime image | Image provenance, rebuilding, patch cadence |
| Runtime configuration service | Environment settings and feature control | Access, refresh behavior, safe defaults |
Do not add another tool if an existing supported platform already owns the desired state. Two controllers repeatedly changing the same setting can create drift rather than remove it.
Read a small Bicep file
This illustrative sandbox template declares a storage account:
param location string = resourceGroup().location
@minLength(3)
@maxLength(24)
param storageName string
resource store 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: storageName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
}
}
output storageId string = store.id
The name must also satisfy the serviceβs lowercase alphanumeric and global-uniqueness requirements. This teaching example does not provision private endpoints, diagnostic settings, lifecycle policy, or every organizational baseline. Do not describe it as a complete production storage design.
Parameters expose supported variation. Outputs let consumers refer to created resource identities. Modules encapsulate related resources behind a smaller interface. Referencing another moduleβs output can express a dependency; independent modules can execute concurrently.
Never put secrets into ordinary outputs merely to make later jobs convenient.
Validate in increasing order of consequence
In a sandbox with the approved Bicep tooling, a learner can compile locally:
az bicep build --file .\main.bicep
Building validates language and produces an ARM template. It does not create resources and does not prove permissions, available names, policy compliance, or runtime connectivity.
With an explicitly selected sandbox subscription/resource group and appropriate access, preview the deployment:
az account show --query "{subscription:name, tenant:tenantId}" -o table
az deployment group what-if `
--resource-group YOUR-SANDBOX-RG `
--template-file .\main.bicep `
--parameters storageName=YOURUNIQUESTORAGENAME
Replace placeholders and confirm the account before running. What-if predicts resource changes without applying them, but it still requires deployment-related permissions. Review creates, modifications, and deletions. Its prediction can contain limitations or noise, especially around runtime-resolved values.
Only an explicitly approved create/apply step changes the infrastructure. A successful what-if is not proof that an application will work after deployment.
Keep configuration consistent across environments
Promote reviewed code with controlled parameter files or runtime configuration, not manual portal edits that nobody can reproduce. Store non-secret configuration in version control as appropriate and retrieve secrets through a protected store.
Separate development/test/production scopes and identities. A staging pipeline should not have production write permission just because both use the same template.
Track drift: compare declared state with real resources and decide whether an emergency manual change should be codified or reverted. Do not blindly overwrite it before understanding why it exists.
An assistant can draft Bicep or explain a what-if diff. Review resource types/API versions, defaults, public exposure, role assignments, deletion behavior, and unrequested resources. Generated IaC that compiles can still create an expensive or insecure design.
A Bicep file compiles and what-if reports expected changes. What is still unproven?
Try it: review without provisioning
Prerequisites: local Bicep tooling for compilation; optional sandbox Azure access for what-if. The exercise does not require applying a deployment.
Compile the sample, inspect the generated template, and identify one missing production concern. Change a non-secret parameter/default and review the diff. If authorized for what-if, verify the subscription and target group, then inspect predicted changes without applying them.
Expected observation: you can distinguish local syntax validation, cloud deployment prediction, actual deployment, and application health.
Cleanup: remove generated local files you do not need. What-if creates no target resources. If you separately apply a sandbox deployment, review costs first and delete only your explicitly created exercise resources afterwards.
Decision check: two tools manage the same setting
Assign one authoritative owner or clearly partition responsibilities. Repeatedly overwriting each otherβs state is not reliable configuration management.