Azure Terraform Patterns Skill
Composable architecture building blocks for Azure Terraform. Complements
iac-terraform-best-practices.instructions.md (style) and azure-defaults skill (naming, tags, regions).
Quick Reference
| Pattern |
When to Use |
Reference |
| Hub-Spoke Networking |
Multi-workload environments with shared services |
references/hub-spoke-pattern.md |
| Private Endpoint Wiring |
Any PaaS service requiring private connectivity |
references/private-endpoint-pattern.md |
| Diagnostic Settings |
Every deployed resource (mandatory) |
references/common-patterns.md |
| Conditional Deployment |
Optional resources controlled by variables |
references/common-patterns.md |
| Module Composition |
Calling multiple AVM modules in root module |
See inline example below |
| Managed Identity |
Any service-to-service authentication |
references/common-patterns.md |
| Budget & Cost Monitoring |
Every deployment (mandatory) |
references/budget-pattern.md |
| Plan Interpretation |
Pre-deployment validation and change analysis |
references/plan-interpretation.md |
| AVM Pitfalls |
Set-type diffs, provider pins, 4.x changes |
references/avm-pitfalls.md |
| AVM Authoring |
AVM certification requirements, compliance |
references/avm-authoring-requirements.md |
| Module Refactoring |
Monolith → module extraction, state migration |
references/refactor-module.md |
Canonical Example — Module Composition
Wire AVM child modules by passing outputs as inputs (module.<name>.<output>); never
hardcode IDs. Pin module versions with ~> X.Y to allow patches but block surprise major
bumps. Full code sample (resource group + key vault) and rationale in
references/module-composition.md.
Rules
- AVM-first: Use
Azure/avm-res-* registry modules over raw azurerm_* resources
- Hub-spoke: Spokes peer to hub only; never spoke-to-spoke
- Private endpoints: Three resources per service — PE, DNS zone, VNet link
- Diagnostics: Every resource MUST have a diagnostic setting → Log Analytics
- Conditional: Use
for_each (keyed) over count (indexed) for named resources
- Identity: SystemAssigned managed identity + RBAC; avoid keys/connection strings
- Provider pin:
~> 4.0 (allows 4.x patches, blocks 5.0)
- Telemetry: Set
enable_telemetry = false in restricted-network environments
- Moved blocks: Use
moved {} when renaming resources to prevent destroy/recreate
- Budget: 3 forecast thresholds (80%/100%/120%); amount and emails MUST be variables
Steps
Applying a Terraform pattern in a root module:
- Identify the pattern — match your need to a row in Quick Reference (hub-spoke, private endpoint, diagnostics, conditional, identity, budget, plan interpretation)
- Load the reference — read the linked
references/*.md; do not load all at once
- Compose AVM modules — wire outputs as inputs (see Canonical Example); never hardcode IDs
- Pin the provider —
~> 4.0 only; do not use >= 3.0 or exact = 4.x.y
- Add diagnostics + budget — every resource gets a diagnostic setting; every deployment gets a budget with 80%/100%/120% forecast alerts
- Plan before apply —
terraform plan -out=plan.tfplan; review for ~/-/+/- operations against references/plan-interpretation.md
- Validate —
terraform fmt -check, terraform validate, npm run validate:terraform, npm run validate:iac-security-baseline
Gotchas
- Set-type phantom diffs —
azurerm_application_gateway, azurerm_lb,
azurerm_network_security_group, azurerm_firewall, azurerm_frontdoor:
adding ONE element causes ALL elements to show ~ changes. Mitigation:
ignore_changes on set-type blocks.
- Provider pin
~> 4.0 is critical — >= 3.0 crosses breaking
versions; = 4.1.0 blocks patches. MUST use ~> 4.0.
for_each over count for named resources — count causes drift
when items are inserted/removed (Terraform reindexes).
Use for_each = toset().
moved block required for renaming — Renaming a resource ID
without a moved {} block causes destroy + recreate.
- azurerm 4.x renamed attributes —
allow_blob_public_access → allow_nested_items_to_be_public;
enable_https_traffic_only → https_traffic_only_enabled;
azurerm_app_service removed → use azurerm_linux_web_app.
Reference Index
| File |
Contents |
references/hub-spoke-pattern.md |
Full hub & spoke VNet + peering HCL |
references/private-endpoint-pattern.md |
PE + DNS zone + VNet link HCL, subresource table |
references/common-patterns.md |
Diagnostics, conditional deployment, module composition, identity |
references/budget-pattern.md |
Consumption budget, forecast alerts, anomaly detection |
references/plan-interpretation.md |
Plan commands, change symbols, red flags, summary script |
references/avm-pitfalls.md |
Set-type diffs, provider pins, tag ignore, moved blocks, 4.x |
references/tf-best-practices-examples.md |
Best-practice code examples, formatting, code review checklist |
references/bootstrap-backend-template.md |
Backend bootstrap template |
references/deploy-script-template.md |
Deployment script template |
references/project-scaffold.md |
Project scaffolding structure |
references/avm-authoring-requirements.md |
AVM certification: 37 requirements, compliance checklist |
references/refactor-module.md |
Module extraction, state migration, refactoring patterns |
references/module-composition.md |
Canonical AVM module composition example with output wiring |
Source: sohamda/apex-try-out-demo-repo — distributed by TomeVault.
1---2name: sohamda-apex-try-out-demo-repo-terraform-patterns3description: Azure Terraform Patterns Skill4---56# Azure Terraform Patterns Skill78Composable architecture building blocks for Azure Terraform. Complements9`iac-terraform-best-practices.instructions.md` (style) and `azure-defaults` skill (naming, tags, regions).1011---1213## Quick Reference1415| Pattern | When to Use | Reference |16| ------------------------ | ------------------------------------------------ | ------------------------------------------ |17| Hub-Spoke Networking | Multi-workload environments with shared services | `references/hub-spoke-pattern.md` |18| Private Endpoint Wiring | Any PaaS service requiring private connectivity | `references/private-endpoint-pattern.md` |19| Diagnostic Settings | Every deployed resource (mandatory) | `references/common-patterns.md` |20| Conditional Deployment | Optional resources controlled by variables | `references/common-patterns.md` |21| Module Composition | Calling multiple AVM modules in root module | See inline example below |22| Managed Identity | Any service-to-service authentication | `references/common-patterns.md` |23| Budget & Cost Monitoring | Every deployment (mandatory) | `references/budget-pattern.md` |24| Plan Interpretation | Pre-deployment validation and change analysis | `references/plan-interpretation.md` |25| AVM Pitfalls | Set-type diffs, provider pins, 4.x changes | `references/avm-pitfalls.md` |26| AVM Authoring | AVM certification requirements, compliance | `references/avm-authoring-requirements.md` |27| Module Refactoring | Monolith → module extraction, state migration | `references/refactor-module.md` |2829---3031## Canonical Example — Module Composition3233Wire AVM child modules by passing outputs as inputs (`module.<name>.<output>`); never34hardcode IDs. Pin module versions with `~> X.Y` to allow patches but block surprise major35bumps. Full code sample (resource group + key vault) and rationale in36[`references/module-composition.md`](references/module-composition.md).3738---3940## Rules4142- **AVM-first**: Use `Azure/avm-res-*` registry modules over raw `azurerm_*` resources43- **Hub-spoke**: Spokes peer to hub only; never spoke-to-spoke44- **Private endpoints**: Three resources per service — PE, DNS zone, VNet link45- **Diagnostics**: Every resource MUST have a diagnostic setting → Log Analytics46- **Conditional**: Use `for_each` (keyed) over `count` (indexed) for named resources47- **Identity**: SystemAssigned managed identity + RBAC; avoid keys/connection strings48- **Provider pin**: `~> 4.0` (allows 4.x patches, blocks 5.0)49- **Telemetry**: Set `enable_telemetry = false` in restricted-network environments50- **Moved blocks**: Use `moved {}` when renaming resources to prevent destroy/recreate51- **Budget**: 3 forecast thresholds (80%/100%/120%); amount and emails MUST be variables5253## Steps5455Applying a Terraform pattern in a root module:56571. **Identify the pattern** — match your need to a row in [Quick Reference](#quick-reference) (hub-spoke, private endpoint, diagnostics, conditional, identity, budget, plan interpretation)582. **Load the reference** — read the linked `references/*.md`; do not load all at once593. **Compose AVM modules** — wire outputs as inputs (see [Canonical Example](#canonical-example--module-composition)); never hardcode IDs604. **Pin the provider** — `~> 4.0` only; do not use `>= 3.0` or exact `= 4.x.y`615. **Add diagnostics + budget** — every resource gets a diagnostic setting; every deployment gets a budget with 80%/100%/120% forecast alerts626. **Plan before apply** — `terraform plan -out=plan.tfplan`; review for `~`/`-`/`+/-` operations against [`references/plan-interpretation.md`](references/plan-interpretation.md)637. **Validate** — `terraform fmt -check`, `terraform validate`, `npm run validate:terraform`, `npm run validate:iac-security-baseline`6465## Gotchas6667- **Set-type phantom diffs** — `azurerm_application_gateway`, `azurerm_lb`,68 `azurerm_network_security_group`, `azurerm_firewall`, `azurerm_frontdoor`:69 adding ONE element causes ALL elements to show `~` changes. Mitigation:70 `ignore_changes` on set-type blocks.71- **Provider pin `~> 4.0` is critical** — `>= 3.0` crosses breaking72 versions; `= 4.1.0` blocks patches. MUST use `~> 4.0`.73- **`for_each` over `count` for named resources** — `count` causes drift74 when items are inserted/removed (Terraform reindexes).75 Use `for_each = toset()`.76- **`moved` block required for renaming** — Renaming a resource ID77 without a `moved {}` block causes destroy + recreate.78- **azurerm 4.x renamed attributes** —79 `allow_blob_public_access` → `allow_nested_items_to_be_public`;80 `enable_https_traffic_only` → `https_traffic_only_enabled`;81 `azurerm_app_service` removed → use `azurerm_linux_web_app`.8283---8485## Reference Index8687| File | Contents |88| ------------------------------------------ | ----------------------------------------------------------------- |89| `references/hub-spoke-pattern.md` | Full hub & spoke VNet + peering HCL |90| `references/private-endpoint-pattern.md` | PE + DNS zone + VNet link HCL, subresource table |91| `references/common-patterns.md` | Diagnostics, conditional deployment, module composition, identity |92| `references/budget-pattern.md` | Consumption budget, forecast alerts, anomaly detection |93| `references/plan-interpretation.md` | Plan commands, change symbols, red flags, summary script |94| `references/avm-pitfalls.md` | Set-type diffs, provider pins, tag ignore, moved blocks, 4.x |95| `references/tf-best-practices-examples.md` | Best-practice code examples, formatting, code review checklist |96| `references/bootstrap-backend-template.md` | Backend bootstrap template |97| `references/deploy-script-template.md` | Deployment script template |98| `references/project-scaffold.md` | Project scaffolding structure |99| `references/avm-authoring-requirements.md` | AVM certification: 37 requirements, compliance checklist |100| `references/refactor-module.md` | Module extraction, state migration, refactoring patterns |101| `references/module-composition.md` | Canonical AVM module composition example with output wiring |102103---104> Source: [sohamda/apex-try-out-demo-repo](https://github.com/sohamda/apex-try-out-demo-repo) — distributed by [TomeVault](https://tomevault.io).105<!-- tomevault:4.0:skill_md:2026-06-15 -->