--- name: avm-tf-azapi description: Use for AVM Terraform AzAPI resources, provider constraints, ARM schemas, parent IDs, resource types, retries, timeouts, response exports, replacement triggers, and ignore_body_changes. --- # AVM Terraform AzAPI Read the current TFFR3-TFFR8, TFNFR38, TFRMFR1, TFRMNFR1, and TFRMNFR2 pages through before implementing or reviewing an AzAPI resource. ## Provider requirements Every new AVM Terraform module repository that deploys Azure resources MUST use AzAPI for every control-plane resource and every supported direct Azure operation. Do not declare or configure `hashicorp/azurerm`, and do not create any `azurerm_*` resource or data source for convenience or ordinary supporting infrastructure. This managed-authoring prohibition applies to the root implementation, submodules, examples, E2E configurations, Terraform tests, fixtures, setup or teardown Terraform, migration examples, documentation examples, and generated snippets. When supporting Terraform needs a direct Azure resource that the module under test does not supply, use an AzAPI resource, data source, or action. TFFR3 requires: ```hcl terraform { required_providers { azapi = { source = "Azure/azapi" version = "~> 2.12" } } } ``` `~> 2.12` means `>= 2.12, < 3.0`. The 2.12 floor is required for `ignore_body_changes`. Every standalone Terraform root that performs a direct Azure operation MUST include `Azure/azapi` in `required_providers`. Use `azapi_resource`, `azapi_data_plane_resource`, `azapi_resource_action`, `azapi_update_resource`, or an AzAPI data source as appropriate. Do not start a new module from an AzureRM implementation and treat migration as future work. `hashicorp/azurerm ~> 4.0` is permitted only when required for a data-plane or other non-ARM operation that genuinely cannot be implemented with those AzAPI resource forms. Each `azurerm_*` resource or data-source block independently scopes to one specific unsupported operation, documents the exact block and why AzAPI cannot implement it with an upstream AzAPI issue or pull request, and is replaced when support ships. Prefer an AVM TFLint override file, but use a justified line-level annotation when it avoids suppressing unrelated findings in the same scope. One valid block does not authorize another. Do not use the exception for any control-plane resource. Follow `avm-tf-tflint`. ## Complete resource pattern For `Microsoft.Example/widgets`, the deterministic TFFR6 key is `example_widgets`: ```hcl resource "azapi_resource" "this" { type = var.resource_types.example_widgets name = var.name parent_id = var.parent_id location = var.location body = { properties = { skuName = var.sku_name } } ignore_body_changes = length(var.ignore_body_changes.example_widgets) > 0 ? var.ignore_body_changes.example_widgets : null replace_triggers_refs = [ "properties.skuName", ] response_export_values = [ "properties.provisioningState", ] retry = var.retry dynamic "timeouts" { for_each = var.timeouts == null ? [] : [var.timeouts] content { create = timeouts.value.create read = timeouts.value.read update = timeouts.value.update delete = timeouts.value.delete } } } ``` The primary resource label is `this`. Satellite resources such as locks, role assignments, and diagnostic settings use descriptive labels. ### Required AzAPI arguments - `type`: always read from `var.resource_types.`. - `response_export_values`: present on every resource, even when empty. - `replace_triggers_refs`: omit when no body paths require replacement. When present, use a non-empty static list of unique, valid JMESPath body paths; do not include `name` or `location`. - `retry`: assigned directly from `var.retry`. - `timeouts`: emitted with a dynamic block from `var.timeouts`. - `ignore_body_changes`: read from the field for this specific resource and collapse `[]` to `null`. - `tags`: set exactly to `var.tags` when the current AVM ruleset capability snapshot marks the resource type as taggable; omit it for unsupported types. The same requirements apply to equivalent AzAPI resource types, not only `azapi_resource`. ## `resource_types` Drop `Microsoft.`, lowercase the provider token without splitting internal capitals, convert each resource path segment to snake case, and join the tokens with underscores: | ARM type | Key | | --- | --- | | `Microsoft.Example/widgets` | `example_widgets` | | `Microsoft.Example/widgets/parts` | `example_widgets_parts` | | `Microsoft.Authorization/roleAssignments` | `authorization_role_assignments` | | `Microsoft.KeyVault/vaults/secrets` | `keyvault_vaults_secrets` | | `Microsoft.Network/virtualNetworks/subnets` | `network_virtual_networks_subnets` | ```hcl variable "resource_types" { type = object({ example_widgets = optional(string, "Microsoft.Example/widgets@2024-01-01") authorization_locks = optional(string, "Microsoft.Authorization/locks@2020-05-01") example_widgets_parts = optional(object({ example_widgets_parts = optional(string) }), {}) }) default = {} nullable = false description = <