|
| 1 | +# AVM Standard Interfaces |
| 2 | + |
| 3 | +AVM defines a small, fixed set of **interfaces** that resource modules expose where the underlying Azure resource supports them. They standardise variable names, types, and behaviour across every module so consumers learn one shape and reuse it everywhere. |
| 4 | + |
| 5 | +All interfaces are implemented in the canonical utility module **`Azure/avm-utl-interfaces/azure`** (version `~> 0.6`). Resource modules should compose that utility module rather than redefining variable shapes by hand. For the authoritative interface text, fetch each interface page via `https://azure.github.io/Azure-Verified-Modules/llms.txt`. |
| 6 | + |
| 7 | +## Interfaces |
| 8 | + |
| 9 | +### Diagnostic settings |
| 10 | + |
| 11 | +Exposes `diagnostic_settings` (map of objects) so consumers can route resource logs and metrics to Log Analytics, Storage, Event Hub, or partner solutions. Apply on every resource that produces diagnostic logs or metrics. |
| 12 | + |
| 13 | +**Modules MUST use the v2 schema.** The v2 shape models `logs` and `metrics` as sets of objects with `category` / `category_group`, `enabled`, and optional `retention_policy`, instead of the legacy flat sets of category strings. Do not specify allowed values for category names — the module must stay evergreen as resource providers add new categories. |
| 14 | + |
| 15 | +Wire it through the utility module via its `diagnostic_settings_v2` input and consume the `diagnostic_settings_azapi_v2` output — the legacy `diagnostic_settings` input/output on the utility module is the old shape and **must not** be used in new code. |
| 16 | + |
| 17 | +### Role assignments |
| 18 | + |
| 19 | +Exposes `role_assignments` (map of objects) so consumers can attach Azure RBAC role assignments scoped to the resource. The key becomes a stable map key for plan-time stability; the object specifies `role_definition_id_or_name`, `principal_id`, `description`, etc. |
| 20 | + |
| 21 | +### Locks |
| 22 | + |
| 23 | +Exposes a `lock` (single object: `kind` = `None | CanNotDelete | ReadOnly`, `name` optional). Apply to top-level resources that support management locks. |
| 24 | + |
| 25 | +### Managed identities |
| 26 | + |
| 27 | +Exposes `managed_identities` (object with `system_assigned` bool and `user_assigned_resource_ids` set of strings). Apply on every resource that supports managed identity. |
| 28 | + |
| 29 | +### Private endpoints |
| 30 | + |
| 31 | +Exposes `private_endpoints` (map of objects) so consumers can deploy private endpoints into a target subnet, register them in private DNS zones, and configure custom IP addressing. Apply on every resource that supports Private Link. |
| 32 | + |
| 33 | +### Customer-managed keys |
| 34 | + |
| 35 | +Exposes `customer_managed_key` (single object: `key_vault_resource_id`, `key_name`, optional `key_version`, optional `user_assigned_identity` reference). Apply on every resource that supports CMK encryption. |
| 36 | + |
| 37 | +### Tags |
| 38 | + |
| 39 | +Exposes `tags` (map of strings). Required on every resource that supports tags. The pattern-module equivalent typically forwards a shared `tags` input to every composed module. |
| 40 | + |
| 41 | +### AzAPI resource types |
| 42 | + |
| 43 | +Exposes `resource_types` (object) so consumers can pin the API version used for each `azapi_resource` the module owns. The keys are **module-specific** — declare one `optional(string, "<api-version>")` field per `azapi_resource` (or equivalent) the module declares, defaulting each to the latest tested API version. Defaults **MUST** be a stable (non-preview) API version unless the resource only ships preview. |
| 44 | + |
| 45 | +Parent modules **MUST** cascade the relevant subset of `resource_types` to each submodule (see TFFR6 and TFRMNFR1). Submodules **MUST** declare their own `resource_types` variable using the same pattern. |
| 46 | + |
| 47 | +### AzAPI retry |
| 48 | + |
| 49 | +Exposes `retry` (single object: `error_message_regex`, `interval_seconds`, `max_interval_seconds`, all optional). MUST be applied to every `azapi_resource` (and equivalent AzAPI resources) declared by the module and cascaded unchanged into every submodule. `retry` is an attribute on `azapi_resource`, so it is assigned directly. |
| 50 | + |
| 51 | +### AzAPI timeouts |
| 52 | + |
| 53 | +Exposes `timeouts` (single object: `create`, `read`, `update`, `delete`, all optional Go duration strings). MUST be applied to every `azapi_resource` (and equivalent AzAPI resources) declared by the module and cascaded unchanged into every submodule. `timeouts` is a **block** on `azapi_resource` (not an attribute), so a `dynamic "timeouts"` block is required to honour the variable's `null` default. |
| 54 | + |
| 55 | +For the full `retry` and `timeouts` variable schemas, the resource-side wiring (including the `dynamic` block), module-level defaults guidance, and submodule cascade pattern, read [AzAPI.md](./AzAPI.md#retry-and-timeouts). |
| 56 | + |
| 57 | +## Implementation pattern |
| 58 | + |
| 59 | +1. Take a dependency on `Azure/avm-utl-interfaces/azure` in `terraform.tf`. |
| 60 | +2. Call the utility module in `main.tf` to translate the standard inputs into the AzAPI body shape. Note the diagnostic-settings input field is `diagnostic_settings_v2` (the legacy `diagnostic_settings` field on the utility module is the old shape): |
| 61 | + |
| 62 | + ```hcl |
| 63 | + module "avm_interfaces" { |
| 64 | + source = "Azure/avm-utl-interfaces/azure" |
| 65 | + version = "~> 0.6" |
| 66 | +
|
| 67 | + diagnostic_settings_v2 = var.diagnostic_settings |
| 68 | + diagnostic_settings_scope = azapi_resource.this.id |
| 69 | + managed_identities = var.managed_identities |
| 70 | + # … |
| 71 | + } |
| 72 | + ``` |
| 73 | + |
| 74 | +3. Reference the utility module's outputs in the relevant `azapi_resource` blocks. For diagnostic settings iterate `module.avm_interfaces.diagnostic_settings_azapi_v2`: |
| 75 | + |
| 76 | + ```hcl |
| 77 | + resource "azapi_resource" "diagnostic_settings" { |
| 78 | + for_each = module.avm_interfaces.diagnostic_settings_azapi_v2 |
| 79 | +
|
| 80 | + type = each.value.type |
| 81 | + name = each.value.name |
| 82 | + parent_id = each.value.parent_id |
| 83 | + body = each.value.body |
| 84 | + } |
| 85 | + ``` |
| 86 | + |
| 87 | +4. Implement supporting child resources (private endpoints, diagnostic settings, role assignments, locks) as separate `azapi_resource` blocks driven by the same input maps — never collapse them into the parent `body` (TFRMNFR1). |
| 88 | + |
| 89 | +## When NOT to expose an interface |
| 90 | + |
| 91 | +If the underlying Azure resource does not support a capability (e.g. no private endpoints, no managed identity, no diagnostic settings), do **not** expose the corresponding variable. Adding a no-op variable creates a misleading contract. |
0 commit comments