# 3005 Terraform Policy Complianceinstructions 6827f5c2

> MANDATORY Azure Policy compliance rules for Terraform code generation. Azure Policy always wins.

- Skill: `tools-only/3005-terraform-policy-complianceinstructions-6827f5c2` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/3005-terraform-policy-complianceinstructions-6827f5c2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/3005-terraform-policy-complianceinstructions-6827f5c2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/tools-only/3005-terraform-policy-complianceinstructions-6827f5c2

---


# Terraform Policy Compliance Instructions

**First Principle: Azure Policy always wins.** Current Azure Policy
implementation cannot be changed. Code MUST adapt to policy, never
the reverse.

## Mandate

ALL Terraform code generation MUST cross-reference
`04-governance-constraints.md` and `04-governance-constraints.json`
before writing templates. These artifacts contain the discovered
Azure Policy constraints for the target subscription.

## Dynamic Tag List

Tags MUST come from governance constraints, not hardcoded defaults.
The 4 baseline defaults in `terraform-code-best-practices.instructions.md`
(`Environment`, `ManagedBy`, `Project`, `Owner`) are a **MINIMUM** —
discovered policies always win. If governance constraints specify
9 tags, the Terraform code MUST include all 9.

### Example

```text
Defaults (azure-defaults skill):  4 tags
Governance constraints discovered: 9 tags (environment, owner,
  costcenter, application, workload, sla, backup-policy,
  maint-window, tech-contact)
Required in Terraform code:       9 tags (governance wins)
```

```hcl
# locals.tf — all 9 tags when governance requires them
locals {
  tags = merge(var.tags, {
    environment      = var.environment
    owner            = var.owner
    costcenter       = var.costcenter
    application      = var.application
    workload         = var.workload
    sla              = var.sla
    backup-policy    = var.backup_policy
    maint-window     = var.maint_window
    tech-contact     = var.tech_contact
  })
}
```

## `azurePropertyPath` Translation

For each Deny or Modify policy in `04-governance-constraints.json`,
read the `azurePropertyPath` field and translate it to the
corresponding `azurerm_*` resource argument:

### Translation Pattern

1. Split `azurePropertyPath` on `.` → `[resourceType, "properties", ...rest]`
2. Map `resourceType` to the correponding `azurerm_*` resource using the table below
3. Map the `properties.` path to the Terraform argument name (snake_case)

### Resource Type Mapping

| `azurePropertyPath` prefix | Terraform resource                                  |
| -------------------------- | --------------------------------------------------- |
| `storageAccount`           | `azurerm_storage_account`                           |
| `keyVault`                 | `azurerm_key_vault`                                 |
| `sqlServer`                | `azurerm_mssql_server`                              |
| `sqlDatabase`              | `azurerm_mssql_database`                            |
| `cosmosDbAccount`          | `azurerm_cosmosdb_account`                          |
| `webApp`                   | `azurerm_linux_web_app` / `azurerm_windows_web_app` |
| `appServicePlan`           | `azurerm_service_plan`                              |
| `containerRegistry`        | `azurerm_container_registry`                        |
| `aksCluster`               | `azurerm_kubernetes_cluster`                        |
| `serviceBusNamespace`      | `azurerm_servicebus_namespace`                      |
| `eventHubNamespace`        | `azurerm_eventhub_namespace`                        |
| `logAnalyticsWorkspace`    | `azurerm_log_analytics_workspace`                   |

### Property Path Mapping Examples

| `azurePropertyPath`                                  | Terraform Argument                        |
| ---------------------------------------------------- | ----------------------------------------- |
| `storageAccount.properties.minimumTlsVersion`        | `min_tls_version`                         |
| `storageAccount.properties.allowBlobPublicAccess`    | `allow_nested_items_to_be_public`         |
| `storageAccount.properties.supportsHttpsTrafficOnly` | `https_traffic_only_enabled`              |
| `sqlServer.properties.minimalTlsVersion`             | `minimum_tls_version`                     |
| `sqlServer.properties.publicNetworkAccess`           | `public_network_access_enabled`           |
| `keyVault.properties.enableSoftDelete`               | `soft_delete_retention_days` (> 0 = true) |
| `keyVault.properties.enablePurgeProtection`          | `purge_protection_enabled`                |
| `containerRegistry.properties.publicNetworkAccess`   | `public_network_access_enabled`           |
| `webApp.properties.httpsOnly`                        | `https_only`                              |

## Policy Compliance Checklist

For every policy in `04-governance-constraints.json`:

### Deny Policies

- [ ] Read `azurePropertyPath` and `requiredValue` from JSON
- [ ] Translate `azurePropertyPath` to `azurerm_*` resource argument using the table above
- [ ] Verify the generated Terraform code sets the argument to the required value
- [ ] If the argument is missing, add it
- [ ] If the argument value conflicts, change it to match policy

### Modify Policies

- [ ] Document expected auto-modifications in the implementation reference
- [ ] Do NOT set values that Modify policies auto-apply (avoid conflicts)

### DeployIfNotExists Policies

- [ ] Document auto-deployed resources in the implementation reference
- [ ] Include expected resources in cost estimates

### Audit Policies

- [ ] Add a comment in the resource block for awareness
- [ ] Set compliant values where feasible (best effort)

### Audit Policy Comment Pattern

```hcl
resource "azurerm_storage_account" "this" {
  # AUDIT: Policy "Require diagnostic settings" — compliant if Log Analytics configured
  # ...
}
```

## Enforcement Rule

> [!CAUTION]
> **Azure Policy always wins.** Current Azure Policy implementation
> cannot be changed. Code MUST adapt to policy, never the reverse.
> A governance compliance failure is a HARD GATE — the Terraform Code
> Generator MUST NOT proceed past Phase 1.5 with unresolved
> policy violations.

## HCP Terraform Cloud Guardrail

> [!CAUTION]
> **NEVER** use HCP Terraform Cloud (`terraform { cloud {} }`) or
> reference `TFE_TOKEN` in generated code. State backend MUST be
> Azure Storage Account. See
> `terraform-code-best-practices.instructions.md` § State Backend.

## Anti-Patterns

| Anti-Pattern                                                        | Why It Fails                                                                              | Correct Approach                                        |
| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| Use `terraform { cloud {} }` or `TFE_TOKEN`                         | Vendor lock-in; violates Azure-only backend policy                                        | Use `backend "azurerm"` with Azure Storage Account      |
| Assume 4 tags are sufficient                                        | Azure Policy may enforce 9+ tags                                                          | Read `04-governance-constraints.md` for actual tag list |
| Ignore `public_network_access_enabled` constraints                  | Deny policy blocks deployment                                                             | Check network policies in governance constraints        |
| Skip constraints reading ("trust artifact chain")                   | Trusting the chain means accepting architecture decisions, NOT skipping compliance checks | Always read and enforce governance constraints          |
| Hardcode security settings without checking policy                  | Policy may require stricter values                                                        | Cross-reference `04-governance-constraints.json`        |
| Use `bicepPropertyPath` for Terraform translation                   | Bicep path format is ARM-only                                                             | Use `azurePropertyPath` for Terraform argument mapping  |
| Generate Terraform without reading `04-governance-constraints.json` | Governance-blind code fails deployment                                                    | Phase 1.5 is a HARD GATE                                |

## Cross-References

- **Governance constraints artifact**: `agent-output/{project}/04-governance-constraints.md`
- **Governance constraints JSON**: `agent-output/{project}/04-governance-constraints.json`
- **Governance discovery instructions**: `.github/instructions/governance-discovery.instructions.md`
- **Azure defaults (baseline tags)**: `.github/skills/azure-defaults/SKILL.md`
- **Terraform best practices**: `.github/instructions/terraform-code-best-practices.instructions.md`

