This repository contains the TFLint ruleset for Azure Verified Modules.
The avm_azapi_resource_tags_required rule uses a standalone, embedded snapshot at
internal/tagcapability/data/azapi_tags_v1.json; it does not download schemas
at runtime or depend on the AzAPI provider. The snapshot records its
MIT-licensed upstream sources and exact revisions. The weekly/manual
Update AzAPI Tag Snapshot
workflow regenerates it from azure-rest-api-specs through
bicep-types-az, validates it, and opens or updates a deterministic PR only
when the data changes.
For writable resource types, the rule requires a consumer-settable tags
argument but permits any Terraform expression so modules can implement the
standard module-wide fallback and per-resource override contract. Resource
types with read-only or unsupported tags must omit the argument.
When a module exposes per-resource or submodule tag overrides, the
avm_interface_resource_tags rule validates the typed recursive
resource_tags interface. The interface separates resource block labels under
resources from child module labels under modules; omitted or null resource
maps inherit var.tags, while supplied maps replace it without merging, and
{} deliberately removes all tags.
- TFLint v0.62+
- Go v1.26+
You can install a released plugin with tflint --init. Declare a config in .tflint.hcl, replacing X.Y.Z with the release version without the v prefix:
plugin "avm" {
enabled = true
version = "X.Y.Z"
source = "github.com/Azure/tflint-ruleset-avm"
signature = "attestation"
}Every rule name uses avm_<domain>_<subject>_<requirement>-style lowercase snake_case. Rules are enabled by default and preserve their documented severity unless overridden:
rule "avm_interface_resource_types" {
enabled = true
severity = "notice"
}The supported severity values are error, warning, and notice. Any other value is rejected as invalid configuration.
Every AVM rule accepts an optional ignored_resources array. Each selector excludes only the matching static resource or data block from that rule's resource checks; it does not suppress unrelated provider, variable, module, or file diagnostics.
Use Terraform-native managed-resource addresses by default, with data. for data sources:
rule "avm_interface_resource_types" {
enabled = true
ignored_resources = [
"azapi_resource.telemetry",
"data.azapi_resource.telemetry",
]
}resource.azapi_resource.telemetry is accepted as an equivalent explicit managed-resource form. Selectors must include the namespace (when using data.), type, and block name. They apply to static block addresses only: count and for_each blocks are evaluated as declared rather than expanded from Terraform state. Rules without this option preserve their existing behavior.
The required AzAPI interfaces accept local aliases and composed values, including merge, concat, lookup, and conditional result branches. Conditional predicates and unselected object members do not satisfy forwarding. Wire var.timeouts through the nested timeouts block's operation attributes, not a timeouts argument; nullable inputs may use a dynamic block forwarding through its iterator's value. The ignore_body_changes interface applies only to managed azapi_resource blocks; other AzAPI resource kinds do not support it.
avm_provider_azurerm_version_constraint requires declared AzureRM constraints to include 4.999.0 and exclude 5.0.0 and later. Use ~> 4.0 or a compatible range such as >= 4.2, < 5.0. Open-ended ranges and ranges ending at < 6.0 fail even with individual 5.x versions excluded. AzureRM remains optional for AzAPI-only modules; source checks and the existing rejection of narrow 4.x pins are unchanged.
This upper-bound check requires a plugin release containing the fix; v1.2.0 and earlier do not enforce it. ModTM and AzAPI version policies are unchanged.
avm_output_resource_id_required applies to resource-module roots, not pattern or utility roots. It defaults to resource when module_class is omitted, including when metadata.json is missing or malformed. Configure non-resource roots explicitly in .tflint.hcl:
rule "avm_output_resource_id_required" {
enabled = true
module_class = "pattern"
}Use module_class = "utility" for a utility root or module_class = "resource" to state the default explicitly. Other values are rejected. This option affects only this rule; child modules are still ignored and severity overrides continue to work. Module class is not inferred from the process directory, repository name, or optional metadata. Set the appropriate rule configuration for each linted root, including when using --chdir or --recursive.
The module_class option requires a plugin release containing this change. Existing releases such as v1.0.0 and v1.1.0 reject it; do not add it to configurations pinned to those versions.
avm_terraform_module_source_required defaults to module_scope = "root", allowing direct local submodules through ./modules/<name>. When linting a direct modules/* scope independently, configure:
rule "avm_terraform_module_source_required" {
enabled = true
module_scope = "submodule"
}This permits direct sibling sources such as ../route-table-route, as specified by TFRMNFR1. Local paths must contain exactly one directory name after the scope's prefix; a single trailing / is allowed. Nested roots, extra traversal, backslash separators, drive-qualified names, and names ending in a dot or space are rejected. Root scopes still reject sibling paths, and submodule scopes reject ./modules/<name>. Other module_scope values are configuration errors; severity overrides remain supported.
Set the scope for each lint target, including with --chdir or --recursive; it is not inferred from the working directory or TFLint's module call tree. The caller is responsible for selecting only direct modules/* roots. Existing external AVM Registry references, including Registry package submodules, remain accepted. This rule has no repository identity with which to distinguish an external Registry submodule from a same-repository reference. Example configurations that disable this rule are unchanged.
The module_scope option requires a published plugin release containing this fix. Releases through v1.3.0 do not support it; do not add it to configurations pinned to those releases.
See RULES.md for the full rules reference (same content, kept in sync automatically).
This release removes the old names without compatibility aliases.
| Old name | New name |
|---|---|
azapi_data_response_export_values |
avm_azapi_data_response_export_values_required |
azapi_replace_triggers_refs |
avm_azapi_replace_triggers_refs_valid |
azapi_resource_tag |
avm_azapi_resource_tags_required |
azapi_response_export_values |
avm_azapi_response_export_values_required |
customer_managed_key |
avm_interface_customer_managed_key |
deprecated_lock_interface |
avm_interface_lock_deprecated |
deprecated_private_endpoints_interface |
avm_interface_private_endpoints_deprecated |
deprecated_role_assignments_interface |
avm_interface_role_assignments_deprecated |
diagnostic_settings |
avm_interface_diagnostic_settings |
ignore_body_changes |
avm_interface_ignore_body_changes |
location |
avm_interface_location |
lock |
avm_interface_lock |
managed_identities |
avm_interface_managed_identities |
no_entire_resource_output_tffr2 |
avm_output_entire_resource_disallowed |
private_endpoints |
avm_interface_private_endpoints |
private_endpoints_manage_dns_zone_group |
avm_interface_private_endpoints_manage_dns_zone_group |
provider_azapi_version_constraint |
avm_provider_azapi_version_constraint |
provider_azurerm_disallowed |
avm_provider_azurerm_disallowed |
provider_azurerm_version_constraint |
avm_provider_azurerm_version_constraint |
provider_modtm_version_constraint |
avm_provider_modtm_version_constraint |
required_module_source_tffr1 |
avm_terraform_module_source_required |
required_module_source_tfnfr10 |
avm_terraform_ignore_changes_unquoted_references |
required_output_rmfr7 |
avm_output_resource_id_required |
resource_types |
avm_interface_resource_types |
retry |
avm_interface_retry |
role_assignments |
avm_interface_role_assignments |
tags |
avm_interface_tags |
terraform_heredoc_usage |
avm_terraform_literal_heredoc_disallowed |
terraform_module_provider_declaration |
avm_terraform_provider_block_disallowed |
terraform_sensitive_variable_no_default |
avm_terraform_sensitive_variable_default_disallowed |
terraform_tf_file |
avm_terraform_configuration_file_required |
timeouts |
avm_interface_timeouts |
The full list of rules is generated automatically into RULES.md and mirrored into the ## Rules section above.
To regenerate after adding, removing, or modifying rules, run:
./scripts/update-readme.shThis script:
- Runs
go generate ./..., which executescmd/rulesdocto refreshRULES.mdfromrules.Rules. - Splices the rules table from
RULES.mdinto the<!-- RULES:START -->/<!-- RULES:END -->markers inREADME.md.
A GitHub Actions workflow (.github/workflows/rules-docs-check.yml) runs the same script on pull requests that touch rule sources and fails the build if README.md or RULES.md is out of date, so drift cannot land on main.
Clone the repository locally and run the following command:
makeYou can easily install the built plugin with the following:
make installYou can run the built plugin like the following:
$ cat << EOS > .tflint.hcl
plugin "avm" {
enabled = true
}
EOS
$ tflint- Create and publish a GitHub pre-release with a
vX.Y.Ztag and release notes. - Queue the
tflint-ruleset-avmrelease pipeline ingithub-private/azure/Azure-Verified-Moduleswith that tag. - The pipeline builds every target in
release/targets.json, Authenticode-signs the Windows binaries, packages and validates the assets, replaces the pre-release assets, and promotes the pre-release. - Promotion triggers
sign-checksums.yml, which downloads the published release's assets by ID, validates them against the release tag, and creates a GitHub Artifact Attestation forchecksums.txt.
Release pipelines are manually queued and never run from a GitHub push or tag.