Stack conventions
Bicep (Azure)
Azure infrastructure in Bicep, including the subscription-scope infra/shared and infra/app deployments Yavaren generates for new applications.
Azure infrastructure in Bicep, including the subscription-scope infra/shared and infra/app deployments Yavaren generates for new applications.
Added to any stack when the change touches: **/*.bicep, **/*.bicepparam.
Principles
- Keep the repository's deployment layout; for Yavaren-generated applications that is infra/shared (the registry) and infra/app (one environment) as subscription-scope deployments with JSON parameter files per environment, run through ci/iac.sh.
- Build and lint every Bicep file (az bicep build, az bicep lint) on every change, and review a what-if before any deployment.
- Describe every parameter with @description and constrain it with decorators (@minLength, @maxLength, @minValue, @maxValue, @allowed sparingly) or user-defined types.
- Mark every secret parameter @secure() and never return secrets in outputs; pass secrets from Key Vault (getSecret or Key Vault references), not from parameter files.
- Reference resources by symbolic name so dependencies are implicit; use dependsOn only when no reference exists, and use existing for resources deployed elsewhere.
- Use recent, non-preview API versions and keep them consistent across the codebase.
- Compose with modules - one module per cohesive unit (registry access, environment, database) - and prefer Azure Verified Modules over hand-written ones for standard resources.
- Use managed identities and least-privilege role assignments; default to private networking and disable local or key-based authentication where the service allows it.
- Generate deterministic, unique resource names with uniqueString() and a naming convention, and keep environments different only by parameter values.
Conventions
Accessibility
Concurrency
- Give deployments stable names per environment so reruns update the same deployment, and serialise deployments to the same scope in CI.
- Use deployment stacks (az stack sub/group) where the repository manages lifecycle and deletion of resources that leave the template.
Dependencies
- Pin API versions explicitly and update them deliberately (linter rule use-recent-api-versions).
- Pin module references from registries to a version (br/public:avm/res/... :
); do not float to latest. - Pin the Bicep CLI version in CI (az bicep install --version) so builds are reproducible.
Documentation
- Every parameter and output has a @description; modules start with a comment describing what they deploy and at which scope.
- Document required role assignments and pre-existing resources the deployment expects.
Errors
- Fail fast with parameter constraints and user-defined types rather than letting Azure Resource Manager reject the deployment.
- Treat every linter warning as something to fix or to suppress with a #disable-next-line comment and a reason.
Logging
- Send diagnostic settings of each resource to the environment's Log Analytics workspace; set retention per environment.
- Never place secrets in outputs, deployment names or tags; deployment history keeps them.
Naming
- Use lowerCamelCase for parameters, variables, symbolic resource names, outputs and types.
- Do not repeat the resource type in its symbolic name (keyVault, not keyVaultResource); do not use the word name in a symbolic name.
- Build Azure resource names from a prefix plus environment plus uniqueString(...) where global uniqueness is required, respecting each service's length and character rules.
Performance
- Let Azure Resource Manager parallelise; avoid unnecessary dependsOn chains that serialise deployments.
- Keep large deployments split into modules so what-if and failures are easier to read; stay below ARM template limits (for example 800 resources per deployment).
Security
- Use @secure() for passwords, keys and connection strings; never give secure parameters default values containing secrets.
- Store secrets in Key Vault (RBAC authorization mode, purge protection on in production) and let applications read them through managed identity.
- Disable public network access and local authentication (admin users, shared keys) where the service supports it; use private endpoints or VNet integration.
- Use workload identity federation (OIDC) for the pipeline's Azure login instead of client secrets.
- Enable the linter rules secure-secrets-in-params, outputs-should-not-contain-secrets, no-hardcoded-env-urls and use-secure-value-for-secure-inputs.
- Validate parameters with decorators (@allowed, @minLength, @maxLength, @minValue) so a bad value fails before deployment.
- Never build scripts for deploymentScripts or custom script extensions by concatenating parameters; pass them as arguments or environment variables (injection).
Structure
- Order a file as targetScope, imports, parameters, variables, resources and modules, outputs.
- Keep one entry point (main.bicep) per deployment and reusable modules beside it or in a registry (br/ or ts/ references, pinned to a version).
- Keep per-environment values in parameter files (.bicepparam or JSON, consistent with the repository); defaults in the template only for safe, environment-neutral values.
- Keep bicepconfig.json at the root to set linter rule levels and module registry aliases.
Testing
- Run az bicep build and az bicep lint for every file in CI, and az deployment sub what-if (or group what-if) against each environment before deploying.
- Validate rules with PSRule for Azure or Checkov for security and Well-Architected checks.
- Keep a dev environment deployment in the pipeline as the integration test for the templates.
- Unit-level checks are bicep build and lint of every file; integration means a what-if and a deployment of the dev environment from the pipeline.
- Name parameter files and pipeline stages after the environment they deploy (
main.dev.bicepparam) so a failing check points to its case. - Done means tested: new or changed behaviour ships with tests in the same change, and a fixed bug gets a regression test that fails without the fix.
Review checklist
- Do all Bicep files build and pass az bicep lint without new warnings?
- Does every parameter have a @description and appropriate constraints or a user-defined type?
- Are all secret parameters @secure(), with no secrets in parameter files, outputs, tags or deployment names?
- Are dependencies expressed through symbolic references, with dependsOn only where needed?
- Are API versions recent and stable (no preview without a recorded reason)?
- Are managed identities and role assignments least privilege and scoped to the target resources?
- Is public network access and local authentication disabled where the service allows it?
- Do environments differ only by parameter files?
- Are resource names deterministic and within each service's naming rules?
- Are diagnostic settings sent to the environment's Log Analytics workspace?
- Are modules from registries pinned to versions, and are Azure Verified Modules used where they fit?
- Has a what-if been reviewed for each environment affected by the change?
Quality checks (run when your repository has the tool configured)
| Check | Kind | Tool | Blocks the pull request |
|---|---|---|---|
bicep-build |
static | az bicep build | no |
bicep-lint |
lint | az bicep lint | no |
checkov-bicep |
security | checkov | no |
bicep-format |
format | az bicep | no |
gitleaks |
security | gitleaks | no |