Terraform Contribution Guide

Important

While this page describes and summarizes important aspects of contributing to AVM, it only references some of the shared and language specific requirements.

Therefore, this contribution guide MUST be used in conjunction with the Terraform specifications. ALL AVM modules (Resource and Pattern modules) MUST meet the respective requirements described in these specifications!

Summary

This section lists AVM’s Terraform-specific contribution guidance.

Subsections of Terraform Modules

Prerequisites

GitHub Account

To contribute, you need a GitHub account. If you are a Microsoft employee, your account must be linked to your corporate identity and you must be a member of the Azure organization.

Required Tooling

Tip

We recommend Linux, macOS, or Windows Subsystem for Linux (WSL) for your development environment.

Terraform Contribution Flow

This guide covers the end-to-end contribution flow for AVM Terraform modules.
Whether you are a module owner or an external contributor, the core workflow is the same — the key differences are called out using tabs below.

Important

This guide MUST be used in conjunction with the Terraform specifications. All AVM modules must meet the requirements described in those specifications.

Overview


---
config:
  nodeSpacing: 20
  rankSpacing: 20
  diagramPadding: 50
  padding: 5
  flowchart:
    wrappingWidth: 300
    padding: 5
  layout: elk
  elk:
    mergeEdges: true
    nodePlacementStrategy: LINEAR_SEGMENTS
---

flowchart TD
  Z("1 - Fork [optional]")
    click Z "#1-fork-optional"
  A(2 - Branch)
    click A "#2-branch"
  B(3 - Implement your code change)
    click B "#3-implement-your-code-change"
  C(4 - Run avm pre-commit)
    click C "#4-run-avm-pre-commit"
  C2(5 - Run pr-check and e2e tests locally)
    click C2 "#5-run-pr-check-and-e2e-tests-locally"
  D(6 - Raise or Update PR)
    click D "#6-raise-or-update-pr"
  E("7 - Approve and monitor CI tests [owner]")
    click E "#7-approve-and-monitor-ci-tests"
  F{Tests passing?}
  G(8 - Review and merge PR)
    click G "#8-review-and-merge-pr"
  H(9 - Cut a release)
    click H "#9-cut-a-release"
  Z --> A
  A --> B
  B --> C
  C --> C2
  C2 --> D
  D --> E
  E --> F
  F -->|no| B
  F -->|yes| G
  G --> H


1. Fork [optional]

Note

This step is only needed if you do not have write access to the module repository. Module owners and invited collaborators can skip to step 2.

A fork is your own copy of the repository under your GitHub account. It lets you make changes without needing write access to the upstream repo. Once your changes are ready, you raise a pull request from your fork back to the original repository.

  1. Navigate to the module repository in the Azure GitHub organization.

  2. Click the Fork button in the top right.

  3. Select your GitHub account (or organization) as the destination.

  4. Click Create fork.

  5. Clone your fork locally:

    git clone https://github.com/<your-username>/terraform-azurerm-avm-res-<rp>-<modulename>.git
    cd terraform-azurerm-avm-res-<rp>-<modulename>

Keep your fork in sync with the upstream repository before creating a new branch. You can do this from the GitHub UI by clicking Sync fork on your fork’s main page, or locally:

git remote add upstream https://github.com/Azure/terraform-azurerm-avm-res-<rp>-<modulename>.git
git fetch upstream
git checkout main
git merge upstream/main

Use the GitHub CLI to fork and clone in one step. This automatically configures the upstream remote for you:

gh repo fork Azure/terraform-azurerm-avm-res-<rp>-<modulename> --clone
cd terraform-azurerm-avm-res-<rp>-<modulename>

Verify the remotes are set up correctly:

git remote -v
# origin    https://github.com/<your-username>/terraform-azurerm-avm-res-<rp>-<modulename>.git (fetch)
# upstream  https://github.com/Azure/terraform-azurerm-avm-res-<rp>-<modulename>.git (fetch)

2. Branch

Create a branch from main to work on your changes:

git checkout -b <your-branch-name>

If this is a new module and the repository does not exist yet, module owners should first follow the Repository Creation Process.

Note

If the module repository does not exist yet, check the Terraform Resource Modules index for the module owner’s contact details (PrimaryModuleOwnerGHHandle column).


3. Implement your code change

Before writing code, review the Terraform specifications and composition guidelines to ensure your contribution complies with AVM’s design principles.

Once you’ve made your changes, stage, commit, and push them:

git add -A
git commit -m "feat: description of your change"
git push

4. Run avm pre-commit

Before raising a pull request, run pre-commit to update your files:

Important

You need Docker (or Podman) installed and running.

./avm pre-commit

This automatically updates your code formatting, fixes styling issues, and regenerates documentation to meet AVM standards. If pre-commit made any changes, commit and push again:

git add -A
git commit -m "chore: pre-commit fixes"
git push

5. Run pr-check and e2e tests locally

Before raising a PR (or while iterating on one), you can run the same checks that CI will run:

./avm pr-check

This runs static analysis and linting locally so you can catch issues before CI does.

Local e2e testing

You can test your examples locally by running Terraform directly in the examples/ folders:

cd examples/default
az login
terraform init
terraform plan
terraform apply

Use Azure CLI (az login) to authenticate — no environment variables or service principals are needed for local development.

When you’re done, clean up your resources:

terraform destroy

This is especially useful for external contributors, since only module owners can approve CI e2e test runs.

Terraform test (optional)

We support terraform test for unit and integration testing. Golang tests are not supported.

  • Unit tests — place test files in tests/unit. Use mocked providers to keep them fast and free of external dependencies. Run with ./avm tf-test-unit.
  • Integration tests — place test files in tests/integration. These deploy real resources and should be run locally. Run with ./avm tf-test-integration.

6. Raise or Update PR

Tip

Raise your PR early — don’t wait until everything is perfect. An early PR lets you run pr-check and e2e tests in CI and get feedback sooner. You can continue pushing commits to the same branch.

  1. Navigate to the upstream repository on GitHub and click New pull request.
  2. Set the base repository to the upstream AVM repo and base branch to main.
  3. Set your head repository and compare branch to your fork and branch.
  4. Click Create pull request.
  1. Navigate to the repository on GitHub and click New pull request.
  2. Set the base branch to main and the compare branch to your branch.
  3. Click Create pull request.

7. Approve and monitor CI tests

Note

Only module owners can approve CI test runs. External contributors should ensure they have run ./avm pr-check and tested locally before this step.

Once a PR is created, CI workflows are triggered automatically but require a module owner to approve the run. A centrally managed Azure test subscription is provided — no credential configuration is needed.

What CI runs

The pr-check.yml workflow runs two stages:

Linting — static analysis including:

  • avmfix — formatting checks.
  • terraform-docs — documentation is up to date.
  • TFLint — AVM spec compliance.
  • Conftest — checks the plan for Well-Architected Framework compliance using Conftest and OPA.

End-to-end tests — deploys and validates all module examples:

  1. Lists all examples in the examples/ directory.
  2. Tests each example for idempotency (terraform apply then terraform plan).
  3. Destroys all resources (terraform destroy).

If tests fail

Go back to step 3 — fix the issue, run ./avm pre-commit again, push your changes, and the CI tests will re-run automatically on the same PR.

Running e2e for external contributions

When approving a PR from an external contributor:

  1. Review the code for security — check for any malicious code or changes to workflow files before running tests. If found, close the PR and report the contributor.
  2. Create a release branch from main (e.g. release/<description>).
  3. Change the PR’s base branch to the release branch and merge it.
  4. Create a new PR from the release branch to main — this triggers pr-check and e2e tests.
  5. Approve the run and wait for results.
  6. If tests fail, send back to the contributor to fix and repeat from step 3.

Running e2e for your own contributions

For your own PRs, the tests trigger automatically — approve the run and wait for results.


8. Review and merge PR

Every PR must be reviewed and approved before merging.

PR is submitted by a module ownerPR is submitted by anyone, other than the module owner
Module has a single module ownerAVM core team or in case of Terraform only, the owner of another module approves the PRModule owner approves the PR
Module has multiple module ownersAnother owner of the module (other than the submitter) approves the PROne of the owners of the module approves the PR
  • Address any review comments and push updates to your branch.
  • Request a re-review once changes are made.
  • The module owner will merge the PR once approved and tests pass.

For a brand new module being published for the first time, get the module reviewed by the AVM Core team by following the AVM Review Process before merging.

Owner responsibilities


9. Cut a release

Note

This step is performed by the module owner only.

After the PR is merged to main, create a release via GitHub Releases:

  1. Go to the Releases tab and click Draft a new release.
  2. Set Target to the main branch.
  3. Type a new tag (e.g. v0.1.0 for first publish, or increment for subsequent releases). Tags MUST include the v prefix.
  4. Use Generate release notes and credit external contributors.
  5. Click Publish release.

First module publish

For a brand new module, contact the AVM core team (e.g. via the AVM - Module Triage project) to request initial publication to the HashiCorp Registry. Subsequent releases are published automatically.

Important

Continue publishing in the v0.x.y range (e.g., v0.1.0, v0.1.1, v0.2.0) until the AVM team notifies you that v1.0.0 is allowed.


Common mistakes to avoid

  • Search and update TODO comments that come from the template — remove them once addressed.
  • Do not commit terraform.lock.hcl — it is excluded by .gitignore.
  • Update _header.md and SUPPORT.md.
  • Do not commit terraform.tfvars files.

Terraform Composition

Important

This guide MUST be used in conjunction with the Terraform specifications. ALL AVM modules (Resource and Pattern modules) MUST meet the respective requirements described in these specifications!

Repositories

Each Terraform AVM module will have its own GitHub Repository in the Azure GitHub Organization as per SNFR19.

This repo will be created by the Module Owners and the AVM Core team collaboratively, including the configuration of permissions as per SNFR9

Directory and File Structure

Below is the directory and file structure expected for each AVM Terraform repository/module.
See template repo here.

  • tests/ - (for unit tests and integration tests using Terraform test)
    • unit/ - (optional .tftest.hcl files for unit testing with Terraform test)
    • integration/ - (optional .tftest.hcl files for integration testing with Terraform test)
  • modules/ - (for sub-modules only if used)
  • examples/ - (all examples must deploy successfully without requiring input - these are customer facing)
    • <at least one folder> - (at least one example that uses the variable defaults minimum/required parameters/variables only)
    • <other folders for examples as required>
  • /... - (Module files that live in the root of module directory)
    • _header.md - (required for documentation generation)
    • _footer.md - (required for documentation generation)
    • main.tf
    • locals.tf
    • variables.tf
    • outputs.tf
    • terraform.tf
    • README.md (autogenerated)
    • main.resource1.tf (If a larger module you may chose to use dot notation for each resource)
    • locals.resource1.tf

Code Styling

This section points to conventions to be followed when developing a module.

Casing

Use snake_casing as per TFNFR3.

Input Parameters and Variables

Make sure to review all specifications of Category: Inputs/Outputs within the Terraform specification pages.

Tip

See examples in specifications SNFR14 and TFFR14.

Resources

Resources are primarily leveraged by resource modules to declare the primary resource of the main resource type deployed by the AVM module.

Make sure to review all specifications covering resource properties and usage.

Tip

See examples in specifications SFR1 and RMFR1.

Outputs

Make sure to review all specifications of Category: Inputs/Outputs within the Terraform specification pages.

Tip

See examples in specification RMFR7 and TFFR2.

Interfaces

Note

This section is only relevant for contributions to resource modules.

To meet RMFR4 and RMFR5 AVM resource modules must leverage consistent interfaces for all the optional features/extension resources supported by the AVM module primary resource.

Please refer to the Terraform Interfaces page.

Telemetry

To meet the requirements of SFR3 & SFR4, we use the modtm telemetry provider. This lightweight telemetry provider sends telemetry data to Azure Application Insights via a HTTP POST front end service.

The modtm telemetry provider is included in all Terraform modules and is enabled by default through the main.telemetry.tf file being automatically distributed from the template repo. You do not need to change this configuration.

Make sure that the modtm provider is listed under the required_providers section in the module’s terraform.tf file using the following entry. This is also validated by the linter.

terraform {
  required_providers {
    # .. other required providers as needed
    modtm = {
      source = "Azure/modtm"
      version = "~> 0.3"
    }
  }
}

Review of Terraform Modules

The AVM module review is a critical step before an AVM Terraform module gets published to the Terraform Registry and made publicly available for customers, partners and wider community to consume and contribute to. It serves as a quality assurance step to ensure that the AVM Terraform module complies with the Terraform specifications of AVM. The below process outlines the steps that both the module owner and module reviewer need to follow.

  1. The module owner completes the development of the module in their branch or fork.

  2. The module owner submits a pull request (PR) titled AVM-Review-PR and ensures that all checks are passing on that PR as that is a pre-requisite to request a review.

  3. The module owner assigns the avm-core-team-technical-terraform GitHub team as reviewer on the PR.

  4. The module owner leaves the following comment as it is on the module proposal in the AVM - Module Triage project by searching for their module proposal by name there.

    ➕ AVM Terraform Module Review Request
    I have completed my initial development of the module and I would like to request a review of my module before publishing it to the Terraform Registry. The latest code is in a PR titled [AVM-Review-PR](REPLACE WITH URL TO YOUR PR) on the module repo and all checks on that PR are passing.
  5. The AVM team moves the module proposal from “In Development” to “In Review” in the AVM - Module Triage project.

  6. The AVM team will assign a module reviewer who will open a blank issue on the module titled “AVM-Review” and populate it with the below mark down. This template already marks the specs as compliant which are covered by the checks that run on the PR. There are some specs which don’t need to be checked at the time of publishing the module therefore they are marked as NA.

    ➕ AVM Terraform Module Review Issue

    Dear module owner,

    As per the module ownership requirements and responsibilities at the time of [assignment](REPLACE WITH THE LINK TO THE AVM MODULE PROPOSAL), the AVM Team is opening this issue, requesting you to validate your module against the below AVM specifications and confirm its compliance.

    Please don’t close this issue and merge your AVM-Review-PR until advised to do so. This review is a prerequisite for publishing your module’s v0.1.0 in the Terraform Registry. The AVM team is happy to assist with any questions you might have.

    Requested Actions

    1. Complete the below task list by ticking off the tasks.
    2. Complete the below table by updating the Compliant column with Yes, No or NA as possible values.

    Please use the comments columns to provide additional details especially if the Compliant column is updated to No or NA.

    Tasks

    • Address comments on AVM-Review-PR if any
    • Ensure that all checks on AVM-Review-PR are passing
    • Make sure you have run pre-commit.
    • Tick this to acknowledge specs with comment “Module Owner to action this spec post-publish as appropriate” in the table below.
    • Please update the _header.md file as it contains instructions which - once actioned - need to be replaced with Module Name and Description.
    IDSpecCompliantComments
    1ID: SFR1 - Category: Composition - Preview ServicesNA if no preview services are used
    2ID: SFR2 - Category: Composition - WAF AlignedEnsure only high priority reliability & security recommendations are implemented if any
    3ID: SFR3 - Category: Telemetry - Deployment/Usage Telemetry
    4ID: SFR4 - Category: Telemetry - Telemetry Enablement FlexibilityYesYes if AVM Template Repo has been used
    5ID: SFR5 - Category: Composition - Availability Zones
    6ID: SFR6 - Category: Composition - Data Redundancy
    7ID: SNFR25 - Category: Composition - Resource Naming
    8ID: SNFR1 - Category: Testing - Prescribed TestsYesYes if all e2e test, version-check & linting checks passed
    9ID: SNFR2 - Category: Testing - E2E TestingYesYes if e2e tests passed
    10ID: SNFR3 - Category: Testing - AVM Compliance TestsYesYes if all e2e test, version-check & linting checks passed
    11ID: SNFR4 - Category: Testing - Unit TestsNA if no tests created in tests folder
    12ID: SNFR5 - Category: Testing - Upgrade TestsNAModule Owner to action this spec post-publish as appropriate
    13ID: SNFR6 - Category: Testing - Static Analysis/Linting TestsYesYes if all linting checks passed
    14ID: SNFR7 - Category: Testing - Idempotency TestsYesYes if e2e tests passed
    15ID: SNFR24 - Category: Testing - Testing Child, Extension & Interface ResourcesYesYes if e2e tests passed
    16ID: SNFR8 - Category: Contribution/Support - Module Owner(s) GitHub
    17ID: SNFR20 - Category: Contribution/Support - GitHub Teams Only
    18ID: SNFR9 - Category: Contribution/Support - AVM & PG Teams GitHub Repo Permissions
    19ID: SNFR10 - Category: Contribution/Support - MIT LicensingYesYes if AVM Template Repo has been used
    20ID: SNFR11 - Category: Contribution/Support - Issues Response TimesNAModule Owner to action this spec post-publish as appropriate
    21ID: SNFR12 - Category: Contribution/Support - Versions SupportedNAModule Owner to action this spec post-publish as appropriate
    22ID: SNFR23 - Category: Contribution/Support - GitHub Repo Labels
    23ID: SNFR14 - Category: Inputs - Data Types
    24ID: SNFR22 - Category: Inputs - Parameters/Variables for Resource IDs
    25ID: SNFR15 - Category: Documentation - Automatic Documentation GenerationYesYes if linting / docs check passed
    26ID: SNFR16 - Category: Documentation - Examples/E2EYesYes if e2e tests passed
    27ID: SNFR17 - Category: Release - Semantic VersioningYesYes if version-check check passed
    28ID: SNFR18 - Category: Release - Breaking ChangesNAModule Owner to action this spec post-publish as appropriate
    29ID: SNFR19 - Category: Publishing - Registries TargetedNAModule Owner to action this spec post-publish as appropriate
    30ID: SNFR21 - Category: Publishing - Cross Language CollaborationNAModule Owner to action this spec post-publish as appropriate
    31ID: RMFR1 - Category: Composition - Single Resource Only
    32ID: RMFR2 - Category: Composition - No Resource Wrapper Modules
    33ID: RMFR3 - Category: Composition - Resource Groups
    34ID: RMFR4 - Category: Composition - AVM Consistent Feature & Extension Resources Value AddYesYes if linting / terraform check passed
    35ID: RMFR5 - Category: Composition - AVM Consistent Feature & Extension Resources Value Add Interfaces/SchemasYesYes if linting / terraform check passed
    36ID: RMFR8 - Category: Composition - Dependency on child and other resources
    37ID: RMFR6 - Category: Inputs - Parameter/Variable Naming
    38ID: RMFR7 - Category: Outputs - Minimum Required OutputsYesYes if linting / terraform check passed
    39ID: RMNFR1 - Category: Naming - Module Naming
    40ID: RMNFR2 - Category: Inputs - Parameter/Variable Naming
    41ID: RMNFR3 - Category: Composition - RP CollaborationNAModule Owner to action this spec post-publish as appropriate
    42ID: PMFR1 - Category: Composition - Resource Group CreationNA if this is not a pattern module
    43ID: PMNFR1 - Category: Naming - Module NamingNA if this is not a pattern module
    44ID: PMNFR2 - Category: Composition - Use Resource Modules to Build a Pattern ModuleNA if this is not a pattern module
    45ID: PMNFR3 - Category: Composition - Use other Pattern Modules to Build a Pattern ModuleNA if this is not a pattern module
    46ID: PMNFR4 - Category: Hygiene - Missing Resource Module(s)NA if this is not a pattern module
    47ID: PMNFR5 - Category: Inputs - Parameter/Variable NamingNA if this is not a pattern module
    48ID: TFFR1 - Category: Composition - Cross-Referencing Modules
    49ID: TFFR2 - Category: Outputs - Additional Terraform OutputsYesYes if linting / terraform check passed
    50ID: TFNFR1 - Category: Documentation - Descriptions
    51ID: TFNFR2 - Category: Documentation - Module Documentation GenerationYesYes if linting / docs check passed
    52ID: TFNFR3 - Category: Contribution/Support - GitHub Repo Branch ProtectionYesYes if AVM Template Repo has been used
    53ID: TFNFR4 - Category: Composition - Code Styling - lower snake_casingYesYes if linting / terraform check passed
    54ID: TFNFR5 - Category: Testing - Test ToolingYesYes if linting / terraform check passed
    55ID: TFNFR6 - Category: Code Style - Resource & Data Order
    56ID: TFNFR7 - Category: Code Style - count & for_each Use
    57ID: TFNFR8 - Category: Code Style - Resource & Data Block OrdersYesYes if linting / avmfix check passed
    58ID: TFNFR9 - Category: Code Style - Module Block Order
    59ID: TFNFR10 - Category: Code Style - No Double Quotes in ignore_changes
    60ID: TFNFR11 - Category: Code Style - Null Comparison Toggle
    61ID: TFNFR12 - Category: Code Style - Dynamic for Optional Nested Objects
    62ID: TFNFR13 - Category: Code Style - Default Values with coalesce/try
    63ID: TFNFR14 - Category: Inputs - Not allowed variables
    64ID: TFNFR15 - Category: Code Style - Variable Definition OrderYesYes if linting / avmfix check passed
    65ID: TFNFR16 - Category: Code Style - Variable Naming RulesYesYes if linting / terraform check passed
    66ID: TFNFR17 - Category: Code Style - Variables with DescriptionsYesYes if linting / terraform check passed
    67ID: TFNFR18 - Category: Code Style - Variables with TypesYesYes if linting / terraform check passed
    68ID: TFNFR19 - Category: Code Style - Sensitive Data Variables
    69ID: TFNFR20 - Category: Code Style - Non-Nullable Defaults for collection values
    70ID: TFNFR21 - Category: Code Style - Discourage Nullability by DefaultYesYes if linting / avmfix check passed
    71ID: TFNFR22 - Category: Code Style - Avoid sensitive = falseYesYes if linting / avmfix check passed
    72ID: TFNFR23 - Category: Code Style - Sensitive Default Value ConditionsYesYes if linting / terraform check passed
    73ID: TFNFR24 - Category: Code Style - Handling Deprecated VariablesNAModule Owner to action this spec post-publish as appropriate
    74ID: TFNFR25 - Category: Code Style - Verified Modules RequirementsYesYes if linting / terraform check passed
    75ID: TFNFR26 - Category: Code Style - Providers in required_providersYesYes if linting / terraform check passed
    76ID: TFNFR27 - Category: Code Style - Provider Declarations in ModulesYesYes if linting / terraform check passed
    77ID: TFNFR29 - Category: Code Style - Sensitive Data OutputsYesYes if linting / avmfix check passed
    78ID: TFNFR30 - Category: Code Style - Handling Deprecated OutputsNAModule Owner to action this spec post-publish as appropriate
    79ID: TFNFR31 - Category: Code Style - locals.tf for Locals Only
    80ID: TFNFR32 - Category: Code Style - Alphabetical Local ArrangementYesYes if linting / avmfix check passed
    81ID: TFNFR33 - Category: Code Style - Precise Local Types
    82ID: TFNFR34 - Category: Code Style - Using Feature TogglesNAModule Owner to action this spec post-publish as appropriate
    83ID: TFNFR35 - Category: Code Style - Reviewing Potential Breaking ChangesNAModule Owner to action this spec post-publish as appropriate
    84ID: TFNFR36 - Category: Code Style - Setting prevent_deletion_if_contains_resources
    85ID: TFNFR37 - Category: Code Style - Tool Usage by Module Owner
  7. The module reviewer can update the Compliance column for specs in line 42 to 47 to NA, in case the module being reviewed isn’t a pattern module.

  8. The module reviewer reviews the code in the PR and leaves comments to request any necessary updates.

  9. The module reviewer assigns the AVM-Review issue to the module owner and links the AVM-Review Issue to the AVM-Review-PR so that once the module reviewer approves the PR and the module owner merges the AVM-Review-PR, the AMV-Review issue is automatically closed. The module reviews responds to the module owner’s comment on the Module Proposal in AVM Repo with the following

    ➕ AVM Terraform Module Review Initiation Message
    Thank you for requesting a review of your module. The AVM module review process has been initiated, please perform the **Requested Actions** on the AVM-Review issue on the module repo.
  10. The module owner updates the check list and the table in the AVM-Review issue and notifies the module reviewer in a comment.

  11. The module reviewer performs the final review and ensures that all checks in the checklist are complete and the specifications table has been updated with no requirements having compliance as ‘No’.

  12. The module reviewer approves the AVM-Review-PR, and leaves the following comment on the AVM-Review issue with the following comment.

    ➕ AVM Terraform Module Review Completion Message
    Thank you for contributing this module and completing the review process per AVM specs. The AVM-Review-PR has been approved and once you merge it that will close this AVM-Review issue. Please create a release with an initial minor version of `v0.1.0` (tags **MUST** include the `v` prefix) and then contact the AVM core team to publish this module to the HashiCorp Terraform Registry via HCP Terraform. Please continue publishing future versions in the v0.x.y minor range (e.g., `v0.1.0`, `v0.1.1`, `v0.2.0`, etc.) until the AVM team notifies you that publishing `v1.0.0` is allowed.
    
    **Requested Action**: Once the AVM core team has published the module, please update your [module proposal](REPLACE WITH THE LINK TO THE MODULE PROPOSAL) with the following comment.
    
    "The initial review of this module is complete, and the module has been published to the registry by the AVM core team. Requesting AVM team to close this module proposal and mark the module available in the module index.
    Terraform Registry Link: <REPLACE WITH THE LINK OF THE MODULE IN TERRAFORM REGISTRY>
    GitHub Repo Link: <REPLACE WITH THE LINK OF THE MODULE IN GITHUB>"
  13. Once the module owner perform the requested action in the previous step, the module reviewer updates the module proposal by performing the following steps:

  • Assign label Status: Module Available :green_circle: to the module proposal.
  • Update the module index excel file and CSV file by creating a PR to update the module index and links the module proposal as an issue that gets closed once the PR is merged which will move the module proposal from “In Review” to “Done” in the AVM - Module Triage project.

Advanced Topics & FAQ

This page covers advanced scenarios and frequently asked questions that go beyond the standard contribution flow.


Using a custom Azure test subscription

By default, CI end-to-end tests run against a centrally managed Azure subscription. If your module requires a different environment (e.g. due to quota limits or tenant-level deployments), you can override the defaults.

  1. Create a user-assigned managed identity in your target Azure environment.
  2. Create GitHub federated credentials for the managed identity, using the module’s GitHub organization and repository. Select entity type environment and set the name to test.
  3. Assign appropriate roles to the managed identity.
  4. Elevate your access via the Open Source Portal.
  5. Go to the repository Settings > Environments > test and add the following secrets:
    • ARM_CLIENT_ID_OVERRIDE — Client ID of the managed identity.
    • ARM_TENANT_ID_OVERRIDE — Tenant ID.
    • ARM_SUBSCRIPTION_ID_OVERRIDE — Subscription ID.

Custom variables and secrets for end-to-end tests

The test environment in each module repository has approvals and secrets configured for e2e tests. If you need additional variables or secrets:

  • Add them to the test environment in your repository settings.
  • They must be prefixed with TF_VAR_ — other prefixes will be ignored.

OPA (Conftest) policy exceptions

Conftest checks the plan for compliance with the Well-Architected Framework using OPA policies.

If you get policy failures that need an exception, create a .rego file in the exceptions sub-directory of the relevant example:

package Azure_Proactive_Resiliency_Library_v2
import rego.v1
exception contains rules if {
  rules = ["configure_aks_default_node_pool_zones"]
}

TFLint rule overrides

TFLint checks AVM spec compliance using the AVM custom ruleset.

To override a rule, create one of the following HCL files in the root of your module:

FileScope
avm.tflint.override.hclRoot module
avm.tflint_module.override.hclSubmodules
avm.tflint_example.override.hclExamples

Example:

# Disable the required resource id output rule — this is a pattern module.
rule "required_output_rmfr7" {
  enabled = false
}

Include a comment explaining why the rule is disabled.


Excluding examples from end-to-end testing

Create a file called .e2eignore in the example directory. Its contents should explain why the example is excluded from tests.


Global test setup and teardown

If your module requires setup/teardown across all examples, create:

  • examples/setup.sh — (optional) — runs before all examples.
  • examples/teardown.sh (optional) — runs after all examples.

These scripts are authorized with the same credentials as the examples.


Per-example pre and post scripts

For example-specific setup/teardown:

  • examples/<example_name>/pre.sh (optional) — runs before the example.
  • examples/<example_name>/post.sh (optional) — runs after the example.

These run in the context of the example directory, so relative paths work.


Repository governance PRs

A weekly workflow checks repository contents creates a PR if new files or updates are available. The PR is automatically merged, so there is no action required. Module owners will be informed if there are any one off PRs that require intervention.

These PRs do not change module code, so no new release is needed.


Eventual consistency

The Azure Resource Manager API can be eventually consistent. For example, data plane role assignments may not be available immediately after creation.

Use the AzAPI provider’s retry functionality to handle eventual consistency instead of arbitrary time_sleep delays. The AzAPI provider supports configurable retry with retry blocks that can match on specific error codes, providing a more reliable and efficient approach.

Repository Creation Process

Important

This page is for module owners only. If you are an external contributor, skip to the contribution flow.

Important

If this process is not followed exactly, it may result in your repository and any in-progress code being permanently deleted.

1. Add yourself to the Module Owners Team and Open Source orgs

If you have already completed these steps, skip to step 2.

  1. Open the Open Source Portal and ensure your GitHub account is linked to your Microsoft account.
  2. Open the Open Source Portal and ensure you are a member of the Azure and Microsoft organizations.
  3. Navigate to Core Identity and request access to the Azure Verified Module Owners Terraform entitlement.
Info

Until your entitlement request is approved, you can contribute by using JIT elevation.

2. Gather repository information

You’ll need the following from the module request issue:

InformationDescription
Module nameFormat: avm-<type>-<name> (e.g. avm-res-network-virtualnetwork)
Module owner GitHub handleYour GitHub handle
Module owner display nameFirstname Lastname
Module descriptionAuto-prefixed with Terraform Azure Verified <module-type> Module for ...
Resource provider namespaceResource modules only (e.g. Microsoft.Network)
Resource typeResource modules only (e.g. virtualNetworks)
Alternative namesOptional comma-separated list
Secondary owner handleOptional
Secondary owner display nameOptional

3. Create the repository

Prerequisites:

Clone and prepare

cd ~
git clone "https://github.com/Azure/avm-terraform-governance"
cd ./avm-terraform-governance/tf-repo-mgmt

Authenticate

# GitHub CLI
gh auth login -h "github.com" -w -p "https" -s "delete_repo" -s "workflow" -s "read:user" -s "user:email"

# Azure CLI (if not already logged in)
az login --scope https://graph.microsoft.com/.default --allow-no-subscriptions

Run the creation script

if(!(Test-Path -Path "./scripts/New-Repository.ps1")) {
    Write-Error "This script must be run from the tf-repo-mgmt directory."
    exit 1
}

# Required Inputs
$moduleProvider = "azurerm" # Allowed: azurerm, azapi, azure
$moduleName = "<module name>" # e.g. avm-res-network-virtualnetwork
$moduleDisplayName = "<module description>"
$resourceProviderNamespace = "" # Leave empty for Pattern/Utility modules
$resourceType = "" # Leave empty for Pattern/Utility modules
$ownerPrimaryGitHubHandle = "<github handle>"
$ownerPrimaryDisplayName = "<display name>"

# Optional
$moduleAlternativeNames = ""
$ownerSecondaryGitHubHandle = ""
$ownerSecondaryDisplayName = ""

./scripts/New-Repository.ps1 `
    -moduleProvider $moduleProvider `
    -moduleName $moduleName `
    -moduleDisplayName $moduleDisplayName `
    -resourceProviderNamespace $resourceProviderNamespace `
    -resourceType $resourceType `
    -ownerPrimaryGitHubHandle $ownerPrimaryGitHubHandle `
    -ownerPrimaryDisplayName $ownerPrimaryDisplayName `
    -moduleAlternativeNames $moduleAlternativeNames `
    -ownerSecondaryGitHubHandle $ownerSecondaryGitHubHandle `
    -ownerSecondaryDisplayName $ownerSecondaryDisplayName

Complete Open Source Portal Setup

The script will pause and prompt you to configure the Open Source Portal. Follow the link in the script output.

➕ If you see the Complete Setup link

Click Complete Setup and use the following settings:

QuestionAnswer
Classify the repositoryProduction
Assign a Service tree or Opt-outAzure Verified Modules / AVM
Direct ownersAdd yourself and jaredholgate or mawhi. Add avm-team-module-owners as fallback security group.
Public open source licensed project?Yes
What type of open source?Sample code
LicenseMIT
All code created by your team?Yes
Telemetry?Yes, telemetry
Cryptography?No
Project nameAzure Verified Module (Terraform) for ‘module name
Project version1
Project descriptionAzure Verified Module (Terraform) for ‘module name’. Part of AVM project - https://aka.ms/avm
Business goalsCreate IaC module accelerating Azure deployment using Microsoft best practice.
Used in a Microsoft product?Open source, can be leveraged in Microsoft services.
Security best practice?Yes, use just-in-time elevation
Maintainer / Write permissionsLeave empty
Repository template / .gitignoreUncheck both

Click Finish setup + start business review, then View repository, then Elevate your access.

➕ If you do NOT see the Complete Setup link
  1. Go to the Compliance tab and fill out:
    • Direct owners: Add yourself and jaredholgate or mawhi. Add avm-team-module-owners as fallback.
    • Classify the repository: Production
    • Service tree: Azure Verified Modules / AVM
  2. Go back to Overview and click Elevate your access if available.

Return to the terminal and type yes to complete repository configuration.

The script will automatically:

  • Create a PR to add module metadata to the avm-terraform-governance repository.
  • Create an issue to install the Azure Verified Modules GitHub App.

4. Update the issue status

  1. Add the  Status: Repository Created 📄  label.
  2. Remove the  Status: Ready For Repository Creation 📝  label if present.

5. Wait for the GitHub App

Once installed (usually within 24 hours), the environment sync runs automatically at 15:30 UTC on weekdays to complete the repository setup.