Skip to main content

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.

Source facts

Repository
Cloudgeni-ai/opengeni
Last source activity
August 12, 2026 at 21:11
Detected SKILL.md language
English
Stars
202
Forks
17

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
azure-verified-modules
description
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.
# Azure Verified Modules (AVM) Requirements This guide covers the mandatory requirements for Azure Verified Modules certification. These requirements ensure consistency, quality, and maintainability across Azure Terraform modules. **References:** - [Azure Verified Modules](https://azure.github.io/Azure-Verified-Modules/) - [AVM Terraform Requirements](https://azure.github.io/Azure-Verified-Modules/specs/terraform/) ## Table of Contents - [Module Cross-Referencing](#module-cross-referencing) - [Azure Provider Requirements](#azure-provider-requirements) - [Code Style Standards](#code-style-standards) - [Variable Requirements](#variable-requirements) - [Output Requirements](#output-requirements) - [Local Values Standards](#local-values-standards) - [Terraform Configuration Requirements](#terraform-configuration-requirements) - [Testing Requirements](#testing-requirements) - [Documentation Requirements](#documentation-requirements) - [Breaking Changes & Feature Management](#breaking-changes--feature-management) - [Contribution Standards](#contribution-standards) - [Compliance Checklist](#compliance-checklist) --- ## Module Cross-Referencing **Severity:** MUST | **Requirement:** TFFR1 When building Resource or Pattern modules, module owners **MAY** cross-reference other modules. However: - Modules **MUST** be referenced using HashiCorp Terraform registry reference to a pinned version - Example: `source = "Azure/xxx/azurerm"` with `version = "1.2.3"` - Modules **MUST NOT** use git references (e.g., `git::https://xxx.yyy/xxx.git` or `github.com/xxx/yyy`) - Modules **MUST NOT** contain references to non-AVM modules --- ## Azure Provider Requirements **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:** - Authors **MAY** select either Azurerm, Azapi, or both providers - **MUST** use `required_providers` block to enforce provider versions - **SHOULD** use pessimistic version constraint operator (`~>`) **Example:** ```hcl terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 4.0" } azapi = { source = "Azure/azapi" version = "~> 2.0" } } } ``` --- ## Code Style Standards ### Lower snake_casing **Severity:** MUST | **Requirement:** TFNFR4 **MUST** use lower snake_casing for: - Locals - Variables - Outputs - Resources (symbolic names) - Modules (symbolic names) Example: `snake_casing_example` ### Resource & Data Source Ordering **Severity:** SHOULD | **Requirement:** TFNFR6 - Resources that are depended on **SHOULD** come first - Resources with dependencies **SHOULD** be defined close to each other ### Count & for_each Usage **Severity:** MUST | **Requirement:** TFNFR7 - Use `count` for conditional resource creation - **MUST** use `map(xxx)` or `set(xxx)` as resource's `for_each` collection - The map's key or set's element **MUST** be static literals **Example:** ```hcl resource "azurerm_subnet" "pair" { for_each = var.subnet_map # map(string) name = "${each.value}-pair" resource_group_name = azurerm_resource_group.example.name virtual_network_name = azurerm_virtual_network.example.name address_prefixes = ["10.0.1.0/24"] } ``` ### Resource & Data Block Internal Ordering **Severity:** SHOULD | **Requirement:** TFNFR8 **Order within resource/data blocks:** 1. **Meta-arguments (top)**: - `provider` - `count` - `for_each` 2. **Arguments/blocks (middle, alphabetical)**: - Required arguments - Optional arguments - Required nested blocks - Optional nested blocks 3. **Meta-arguments (bottom)**: - `depends_on` - `lifecycle` (with sub-order: `create_before_destroy`, `ignore_changes`, `prevent_destroy`) Separate sections with blank lines. ### Module Block Ordering **Severity:** SHOULD | **Requirement:** TFNFR9 **Order within module blocks:** 1. **Top meta-arguments**: - `source` - `version` - `count` - `for_each` 2. **Arguments (alphabetical)**: - Required arguments - Optional arguments 3. **Bottom meta-arguments**: - `depends_on` - `providers` ### Lifecycle ignore_changes Syntax **Severity:** MUST | **Requirement:** TFNFR10 The `ignore_changes` attribute **MUST NOT** be enclosed in double quotes. **Good:** ```hcl lifecycle { ignore_changes = [tags] } ``` **Bad:** ```hcl lifecycle { ignore_changes = ["tags"] } ``` ### Null Comparison for Conditional Creation **Severity:** SHOULD | **Requirement:** TFNFR11 For parameters requiring conditional resource creation, wrap with `object` type to avoid "known after apply" issues during plan stage. **Recommended:** ```hcl variable "security_group" { type = object({ id = string }) default = null } ``` ### Dynamic Blocks for Optional Nested Objects **Severity:** MUST | **Requirement:** TFNFR12 Nested blocks under conditions **MUST** use this pattern: ```hcl dynamic "identity" { for_each = <condition> ? [<some_item>] : [] content { # block content } } ``` ### Default Values with coalesce/try **Severity:** SHOULD | **Requirement:** TFNFR13 **Good:** ```hcl coalesce(var.new_network_security_group_name, "${var.subnet_name}-nsg") ``` **Bad:** ```hcl var.new_network_security_group_name == null ? "${var.subnet_name}-nsg" : var.new_network_security_group_name ``` ### Provider Declarations in Modules **Severity:** MUST | **Requirement:** TFNFR27 - `provider` **MUST NOT** be declared in modules (except for `configuration_aliases`) - `provider` blocks in modules **MUST** only use `alias` - Provider configurations **SHOULD** be passed in by module users --- ## Variable Requirements ### Not Allowed Variables **Severity:** 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. ### Variable Definition Order **Severity:** SHOULD | **Requirement:** TFNFR15 Variables **SHOULD** follow this order: 1. All required fields (alphabetical) 2. All optional fields (alphabetical) ### Variable Naming Rules **Severity:** SHOULD | **Requirement:** TFNFR16 - Follow [HashiCorp's naming rules](https://www.terraform.io/docs/extend/best-practices/naming.html) - Feature switches **SHOULD** use positive statements: `xxx_enabled` instead of `xxx_disabled` ### Variables with Descriptions **Severity:** SHOULD | **Requirement:** TFNFR17 - `description` **SHOULD** precisely describe the parameter's purpose and expected data type - Target audience is module users, not developers - For `object` types, use HEREDOC format ### Variables with Types **Severity:** MUST | **Requirement:** TFNFR18 - `type` **MUST** be defined for every variable - `type` **SHOULD** be as precise as possible - `any` **MAY** only be used with adequate reasons - Use `bool` instead of `string`/`number` for true/false values - Use concrete `object` instead of `map(any)` ### Sensitive Data Variables **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. ### Non-Nullable Defaults for Collections **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. ### Discourage Nullability by Default **Severity:** MUST | **Requirement:** TFNFR21 `nullable = true` **MUST** be avoided unless there's a specific semantic need for null values. ### Avoid sensitive = false **Severity:** MUST | **Requirement:** TFNFR22 `sensitive = false` **MUST** be avoided (this is the default). ### Sensitive Default Value Conditions **Severity:** MUST | **Requirement:** TFNFR23 A default value **MUST NOT** be set for sensitive inputs (e.g., default passwords). ### Handling Deprecated Variables **Severity:** MUST | **Requirement:** TFNFR24 - Move deprecated variables to `deprecated_variables.tf` - Annotate with `DEPRECATED` at the beginning of description - Declare the replacement's name - Clean up during major version releases --- ## Output Requirements ### Additional Terraform Outputs **Severity:** 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:** - Output *computed* attributes of resources as discrete outputs (anti-corruption layer pattern) - **SHOULD NOT** output values that are already inputs (except `name`) - Use `sensitive = true` for sensitive attributes - For resources deployed with `for_each`, output computed attributes in a map structure **Examples:** ```hcl # Single resource computed attribute output "foo" { description = "MyResource foo attribute" value = azurerm_resource_myresource.foo } # for_each resources output "childresource_foos" { description = "MyResource children's foo attributes" value = { for key, value in azurerm_resource_mychildresource : key => value.foo } } # Sensitive output output "bar" { description = "MyResource bar attribute" value = azurerm_resource_myresource.bar sensitive = true } ``` ### Sensitive Data Outputs **Severity:** MUST | **Requirement:** TFNFR29 Outputs containing confidential data **MUST** be declared with `sensitive = true`. ### Handling Deprecated Outputs **Severity:** MUST | **Requirement:** TFNFR30 - Move deprecated outputs to `deprecated_outputs.tf` - Define new outputs in `outputs.tf` - Clean up during major version releases --- ## Local Values Standards ### locals.tf Organization **Severity:** MAY | **Requirement:** TFNFR31 - `locals.tf` **SHOULD** only contain `locals` blocks - **MAY** declare `locals` blocks next to resources for advanced scenarios ### Alphabetical Local Arrangement **Severity:** MUST | **Requirement:** TFNFR32 Expressions in `locals` blocks **MUST** be arranged alphabetically. ### Precise Local Types **Severity:** SHOULD | **Requirement:** TFNFR33 Use precise types (e.g., `number` for age, not `string`). --- ## Terraform Configuration Requirements ### Terraform Version Requirements **Severity:** MUST | **Requirement:** TFNFR25 **`terraform.tf` requirements:** - **MUST** contain only one `terraform` block - First line **MUST** define `required_version` - **MUST** include minimum version constraint - **MUST** include maximum major version constraint
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub