npx skills add ...
npx skills add hashicorp/agent-skills --skill azure-verified-modules
Azure Verified Modules (AVM) requirements and best practices for developing certified Azure Terraform modules. Use when creating or reviewing Azure modules that need AVM certification.
npx skills add hashicorp/agent-skills --skill azure-verified-modules
This guide covers the mandatory requirements for Azure Verified Modules certification. These requirements ensure consistency, quality, and maintainability across Azure Terraform modules.
References:
Severity: MUST | Requirement: TFFR1
When building Resource or Pattern modules, module owners MAY cross-reference other modules. However:
source = "Azure/xxx/azurerm" with version = "1.2.3"git::https://xxx.yyy/xxx.git or github.com/xxx/yyy)Severity: MUST | Requirement: TFFR3
Authors MUST only use the following Azure providers:
| Provider | Min Version | Max Version |
|---|---|---|
| azapi | >= 2.0 | < 3.0 |
| azurerm | >= 4.0 | < 5.0 |
Requirements:
required_providers block to enforce provider versions~>)Example:
Severity: MUST | Requirement: TFNFR4
MUST use lower snake_casing for:
Example: snake_casing_example
Severity: SHOULD | Requirement: TFNFR6
Severity: MUST | Requirement: TFNFR7
count for conditional resource creationmap(xxx) or set(xxx) as resource's for_each collectionExample:
Severity: SHOULD | Requirement: TFNFR8
Order within resource/data blocks:
Meta-arguments (top):
providercountfor_eachArguments/blocks (middle, alphabetical):
Meta-arguments (bottom):
depends_onlifecycle (with sub-order: create_before_destroy, ignore_changes, prevent_destroy)Separate sections with blank lines.
Severity: SHOULD | Requirement: TFNFR9
Order within module blocks:
Top meta-arguments:
sourceversioncountfor_eachArguments (alphabetical):
Bottom meta-arguments:
depends_onprovidersSeverity: MUST | Requirement: TFNFR10
The ignore_changes attribute MUST NOT be enclosed in double quotes.
Good:
Bad:
Severity: SHOULD | Requirement: TFNFR11
For parameters requiring conditional resource creation, wrap with object type to avoid "known after apply" issues during plan stage.
Recommended:
Severity: MUST | Requirement: TFNFR12
Nested blocks under conditions MUST use this pattern:
Severity: SHOULD | Requirement: TFNFR13
Good:
Bad:
Severity: MUST | Requirement: TFNFR27
provider MUST NOT be declared in modules (except for configuration_aliases)provider blocks in modules MUST only use aliasSeverity: MUST | Requirement: TFNFR14
Module owners MUST NOT add variables like enabled or module_depends_on to control entire module operation. Boolean feature toggles for specific resources are acceptable.
Severity: SHOULD | Requirement: TFNFR15
Variables SHOULD follow this order:
Severity: SHOULD | Requirement: TFNFR16
xxx_enabled instead of xxx_disabledSeverity: SHOULD | Requirement: TFNFR17
description SHOULD precisely describe the parameter's purpose and expected data typeobject types, use HEREDOC formatSeverity: MUST | Requirement: TFNFR18
type MUST be defined for every variabletype SHOULD be as precise as possibleany MAY only be used with adequate reasonsbool instead of string/number for true/false valuesobject instead of map(any)Severity: SHOULD | Requirement: TFNFR19
If a variable's type is object and contains sensitive fields, the entire variable SHOULD be sensitive = true, or extract sensitive fields into separate variables.
Severity: SHOULD | Requirement: TFNFR20
Nullable SHOULD be set to false for collection values (sets, maps, lists) when using them in loops. For scalar values, null may have semantic meaning.
Severity: MUST | Requirement: TFNFR21
nullable = true MUST be avoided unless there's a specific semantic need for null values.
Severity: MUST | Requirement: TFNFR22
sensitive = false MUST be avoided (this is the default).
Severity: MUST | Requirement: TFNFR23
A default value MUST NOT be set for sensitive inputs (e.g., default passwords).
Severity: MUST | Requirement: TFNFR24
deprecated_variables.tfDEPRECATED at the beginning of descriptionSeverity: SHOULD | Requirement: TFFR2
Authors SHOULD NOT output entire resource objects as these may contain sensitive data and the schema can change with API or provider versions.
Best Practices:
name)sensitive = true for sensitive attributesfor_each, output computed attributes in a map structureExamples:
Severity: MUST | Requirement: TFNFR29
Outputs containing confidential data MUST be declared with sensitive = true.
Severity: MUST | Requirement: TFNFR30
deprecated_outputs.tfoutputs.tfSeverity: MAY | Requirement: TFNFR31
locals.tf SHOULD only contain locals blockslocals blocks next to resources for advanced scenariosSeverity: MUST | Requirement: TFNFR32
Expressions in locals blocks MUST be arranged alphabetically.
Severity: SHOULD | Requirement: TFNFR33
Use precise types (e.g., number for age, not string).
Severity: MUST | Requirement: TFNFR25
terraform.tf requirements:
terraform blockrequired_version~> #.# or >= #.#.#, < #.#.# formatExample:
Severity: MUST | Requirement: TFNFR26
terraform block MUST contain required_providers blocksource and versionsource MUST be in format namespace/nameversion MUST include minimum and maximum major version constraints~> #.# or >= #.#.#, < #.#.# formatSeverity: MUST | Requirement: TFNFR5
Required testing tools for AVM:
terraform validate/fmt/test)Severity: SHOULD | Requirement: TFNFR36
For robust testing, prevent_deletion_if_contains_resources SHOULD be explicitly set to false in test provider configurations.
Severity: MUST | Requirement: TFNFR2
.terraform-docs.yml file MUST be present in the module rootSeverity: MUST | Requirement: TFNFR34
New resources added in minor/patch versions MUST have a toggle variable to avoid creation by default:
Severity: MUST | Requirement: TFNFR35
Breaking changes requiring caution:
Resource blocks:
dynamicmoved blockscount to for_each or vice versaVariable/Output blocks:
typedefault valuesnullable to falsesensitive from false to truedefaultvaluesensitive valueSeverity: MUST | Requirement: TFNFR3
Module owners MUST set branch protection policies on the default branch (typically main):
Use this checklist when developing or reviewing Azure Verified Modules:
.terraform-docs.yml present in module rootfor_each uses map() or set() with static keysignore_changes not quotedcoalesce() or try() used for default valuesenabled or module_depends_on variablesany)nullable = falsesensitive = false declarationsdeprecated_variables.tfsensitive = truedeprecated_outputs.tfterraform.tf has version constraints (~> format)required_providers block present with all providersprovider declarations in module (except aliases)Based on: Azure Verified Modules - Terraform Requirements