Azure Verified Modules
Glossary GitHub GitHub Issues Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Last updated: 01 May 2024

Shared Specification (Bicep & Terraform)

Make sure to checkout the Module Classification Definitions first before reading further so you understand the difference between a Resource and Pattern Module in AVM.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

This page contains the shared requirements for both Bicep and Terraform AVM modules (Resource and Pattern modules) that ALL AVM modules MUST meet. In addition to these, requirements specific to each language, defined in their respective specifications also MUST be met. See Bicep and Terraform specific requirements for more information.

The following table summarizes the category identification codes used in this specification:

ScopeFunctional requirementsNon-functional requirements
Shared requirements (resource & pattern modules)SFRSNFR
Resource module level requirementsRMFRRMNFR
Pattern module level requirementsPMFRPMNFR



Shared Requirements (Resource & Pattern Modules)

Listed below are both functional and non-functional requirements for both classifications of AVM modules (Resource and Pattern) and all languages.

While every effort is being made to standardize requirements and implementation details across all languages, it is expected that some of the specifications will be different between their respective languages to ensure we follow the best practices and leverage features of each language.

Functional Requirements (SFR)

This section includes shared, functional requirements (SFR) for Bicep an Terraform AVM modules (Resource and Pattern).


ID: SFR1 - Category: Composition - Preview Services

Modules MAY create/adopt public preview services and features at their discretion.

Preview API versions MAY be used when:

  • The resource/service/feature is GA but the only API version available for the GA resource/service/feature is a preview version
    • For example, Diagnostic Settings (Microsoft.Insights/diagnosticSettings) the latest version of the API available with GA features, like Category Groups etc., is 2021-05-01-preview
    • Otherwise the latest “non-preview” version of the API SHOULD be used

Preview services and features, SHOULD NOT be promoted and exposed, unless they are supported by the respective PG, and it’s documented publicly.

However, they MAY be exposed at the module owners discretion, but the following rules MUST be followed:

  • The description of each of the parameters/variables used for the preview service/feature MUST start with:
    • “THIS IS A <PARAMETER/VARIABLE> USED FOR A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE PRODUCT DOCS FOR CLARIFICATION”



ID: SFR2 - Category: Composition - WAF Aligned

Modules SHOULD set defaults in input parameters/variables to align to high priority/impact recommendations, where appropriate and applicable, in the following frameworks and resources:

They SHOULD NOT align to these recommendations when it requires an external dependency/resource to be deployed and configured and then associated to the resources in the module.

Alignment SHOULD prioritize best-practices and security over cost optimization, but MUST allow for these to be overridden by a module consumer easily, if desired.

Read the FAQ of What does AVM mean by “WAF Aligned”? for more detailed information and examples.



ID: SFR3 - Category: Telemetry - Deployment/Usage Telemetry

We will maintain a set of CSV files in the AVM Central Repo (Azure/Azure-Verified-Modules) with the required TelemetryId prefixes to enable checks to utilize this list to ensure the correct IDs are used. To see the formatted content of these CSV files with additional information, please visit the AVM Module Indexes page.

These will also be provided as a comment on the module proposal, once accepted, from the AVM core team.

Modules MUST provide the capability to collect deployment/usage telemetry, via a blank ARM deployment, as detailed in Telemetry further. Telemetry data should be collected on the “top level” main.bicep or main.telemetry.tf file; it is not necessary to include it in any nested files (child modules).

To highlight that AVM modules use telemetry, an information notice MUST be included in the footer of each module’s README.md file with the below content. (See more details on this requirement, here.)

The following information notice is automatically added at the bottom of the README.md file of the module when

  • Bicep: Using the avm/utilities/tools/Set-AVMModule.ps1 utility
  • Terraform: Executing the make docs command with the note and header ## Data Collection being placed in the module’s _footer.md beforehand

## Data Collection

The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at <https://go.microsoft.com/fwlink/?LinkID=824704>. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.

The ARM deployment name used for the telemetry MUST follow the pattern and MUST be no longer than 64 characters in length: <AVM 8 chars (alphanumeric)>.<res/ptn>.<(short) module name>.<version>.<uniqueness>

  • <AVM 8 chars (alphanumeric)>
    • Bicep == 46d3xbcp
    • Terraform == 46d3xgtf
  • <res/ptn> == AVM Resource or Pattern Module
  • <(short) module name> == The AVM Module’s, possibly shortened, name including the resource provider and the resource type, without;
    • The prefixes: avm-res- or terraform-<provider>-avm-res-
    • The prefixes: avm-ptn- or terraform-<provider>-avm-ptn-
Due to the 64-character length limit of Azure deployment names, the <(short) module name> segment has a length limit of 36 characters, so if the module name is longer than that, it MUST be truncated to 36 characters. If any of the semantic version’s segments are longer than 1 character, it further restricts the number of characters that can be used for naming the module.
  • <version> == The AVM Module’s MAJOR.MINOR version (only) with . (periods) replaced with - (hyphens), to allow simpler splitting of the ARM deployment name
  • <uniqueness> == This section of the ARM deployment name is to be used to ensure uniqueness of the deployment name.
    • This is to cater for the following scenarios:
      • The module is deployed multiple times to the same:
        • Location/Region
        • Scope (Tenant, Management Group,Subscription, Resource Group)

An example deployment name for the AVM Virtual Machine Resource Module would be:

  • Bicep == 46d3xbcp.res.compute-virtualmachine.1-2-3.eum3
  • Terraform == 46d3xgtf.res.compute-virtualmachine.1-2-3.eum3

An example deployment name for a shortened module name would be:

  • Bicep == 46d3xbcp.res.desktopvirtualization-appgroup.1-2-3.eum3
  • Terraform == 46d3xgtf.res.desktopvirtualization-appgroup.1-2-3.eum3

See the language specific contribution guides for detailed guidance and sample code to use in AVM modules to achieve this requirement.




ID: SFR4 - Category: Telemetry - Telemetry Enablement Flexibility

The telemetry enablement MUST be on/enabled by default, however this MUST be able to be disabled by a module consumer by setting the below parameter/variable value to false:

  • Bicep: enableTelemetry
  • Terraform: enable_telemetry



ID: SFR5 - Category: Composition - Availability Zones

Modules that deploy zone-redundant resources MUST enable the spanning across as many zones as possible by default, typically all 3.

Modules that deploy zonal resources MUST provide the ability to specify a zone for the resources to be deployed/pinned to. However, they MUST NOT default to a particular zone by default, e.g. 1 in an effort to make the consumer aware of the zone they are selecting to suit their architecture requirements.

For both scenarios the modules MUST expose these configuration options via configurable parameters/variables.

For information on the differences between zonal and zone-redundant services, see Availability zone service and regional support



ID: SFR6 - Category: Composition - Data Redundancy

Modules that deploy resources or patterns that support data redundancy SHOULD enable this to the highest possible value by default, e.g. RA-GZRS. When a resource or pattern doesn’t provide the ability to specify data redundancy as a simple property, e.g. GRS etc., then the modules MUST provide the ability to enable data redundancy for the resources or pattern via parameters/variables.

For example, a Storage Account module can simply set the sku.name property to Standard_RAGZRS. Whereas a SQL DB or Cosmos DB module will need to expose more properties, via parameters/variables, to allow the specification of the regions to replicate data to as per the consumers requirements.

For information on the data redundancy options in Azure, see Cross-region replication in Azure



Non-Functional Requirements (SNFR)

This section includes shared, non-functional requirements (SNFR) for Bicep an Terraform AVM modules (Resource and Pattern).


ID: SNFR25 - Category: Composition - Resource Naming

Module owners MUST set the default resource name prefix for child, extension, and interface resources to the associated abbreviation for the specific resource as documented in the following CAF article Abbreviation examples for Azure resources, if specified and documented. This reduces the amount of input values a module consumer MUST provide by default when using the module.

For example, a Private Endpoint that is being deployed as part of a resource module, via the mandatory interfaces, MUST set the Private Endpoint’s default name to begin with the prefix of pep-.

Module owners MUST also provide the ability for these default names, including the prefixes, to be overridden via a parameter/variable if the consumer wishes to.

Furthermore, as per RMNFR2, Resource Modules MUST not have a default value specified for the name of the primary resource and therefore the name MUST be provided and specified by the module consumer.

The name provided MAY be used by the module owner to generate the rest of the default name for child, extension, and interface resources if they wish to. For example, for the Private Endpoint mentioned above, the full default name that can be overridden by the consumer, MAY be pep-<primary-resource-name>.

If the resource does not have a documented abbreviation in Abbreviation examples for Azure resources, then the module owner is free to use a sensible prefix instead.



ID: SNFR1 - Category: Testing - Prescribed Tests

Modules MUST use the prescribed tooling and testing frameworks defined in the language specific specs.




ID: SNFR2 - Category: Testing - E2E Testing

Modules MUST implement end-to-end (deployment) testing that create actual resources to validate that module deployments work. In Bicep tests are sourced from the directories in /tests/e2e. In Terraform, these are in /examples.

Each test MUST run and complete without user inputs successfully, for automation purposes.

Each test MUST also destroy/clean-up its resources and test dependencies following a run.

To see a directory and file structure for a module, see the language specific contribution guide.

Required Resources/Dependencies Required for E2E Tests

It is likely that to complete E2E tests, a number of resources will be required as dependencies to enable the tests to pass successfully. Some examples:

  • When testing the Diagnostic Settings interface for a Resource Module, you will need an existing Log Analytics Workspace to be able to send the logs to as a destination.
  • When testing the Private Endpoints interface for a Resource Module, you will need an existing Virtual Network, Subnet and Private DNS Zone to be able to complete the Private Endpoint deployment and configuration.

Module owners MUST:

  • Create the required resources that their module depends upon in the test file/directory
    • They MUST either use:
      • Simple/native resource declarations/definitions in their respective IaC language,
        OR
      • Another already published AVM Module that MUST be pinned to a specific published version.
        • They MUST NOT use any local directory path references or local copies of AVM modules in their own modules test directory.
Terraform
resource "azurerm_resource_group" "example" {
  name     = "rsg-test-001"
  location = "West Europe"
}

resource "azurerm_log_analytics_workspace" "example" {
  name                = "law-test-001"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  sku                 = "PerGB2018"
  retention_in_days   = 30
}
Bicep
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = {
  name: 'law-test-001'
  location: resourceGroup().location
  properties: {
    sku: {
      name: 'PerGB2018'
    }
    retentionInDays: 30
  }
}



ID: SNFR3 - Category: Testing - AVM Compliance Tests

Modules MUST pass all tests that ensure compliance to AVM specifications. These tests MUST pass before a module version can be published.

Please note these are still under development at this time and will be published and available soon for module owners.

Module owners MUST request a manual GitHub Pull Request review, prior to their first release of version 0.1.0 of their module, from the related GitHub Team: @Azure/avm-core-team-technical-bicep, OR @Azure/avm-core-team-technical-terraform.




ID: SNFR4 - Category: Testing - Unit Tests

Modules SHOULD implement unit testing to ensure logic and conditions within parameters/variables/locals are performing correctly. These tests MUST pass before a module version can be published.

Unit Tests test specific module functionality, without deploying resources. Used on more complex modules. In Bicep and Terraform these live in tests/unit.




ID: SNFR5 - Category: Testing - Upgrade Tests

Modules SHOULD implement upgrade testing to ensure new features are implemented in a non-breaking fashion on non-major releases.




ID: SNFR6 - Category: Testing - Static Analysis/Linting Tests

Modules MUST use static analysis, e.g., linting, security scanning (PSRule, tflint, etc.). These tests MUST pass before a module version can be published.

There may be differences between languages in linting rules standards, but the AVM core team will try to close these and bring them into alignment over time.




ID: SNFR7 - Category: Testing - Idempotency Tests

Modules MUST implement idempotency end-to-end (deployment) testing. E.g. deploying the module twice over the top of itself.

Modules SHOULD pass the idempotency test, as we are aware that there are some exceptions where they may fail as a false-positive or legitimate cases where a resource cannot be idempotent.

For example, Virtual Machine Image names must be unique on each resource creation/update.




ID: SNFR24 - Category: Testing - Testing Child, Extension & Interface Resources

Module owners MUST test that child, extension and interface resources, that are supported by their modules, are tested in E2E tests as per SNFR2 to ensure they deploy and are configured correctly.

These MAY be tested in a separate E2E test and DO NOT have to be tested in each E2E test.




ID: SNFR8 - Category: Contribution/Support - Module Owner(s) GitHub

A module MUST have an owner that is defined and managed by a GitHub Team in the Azure GitHub organization.

Today this is only Microsoft FTEs, but everyone is welcome to contribute. The module just MUST be owned by a Microsoft FTE (today) so we can enforce and provide the long-term support required by this initiative.

The names for the GitHub teams for each approved module are already defined in the respective Module Indexes. These teams MUST be created (and used) for each module.



ID: SNFR20 - Category: Contribution/Support - GitHub Teams Only

All GitHub repositories that AVM module are published from and hosted within MUST only assign GitHub repository permissions to GitHub teams only.

Each module MUST have separate GitHub teams assigned for module owners AND module contributors respectively. These GitHub teams MUST be created in the Azure organization in GitHub.

There MUST NOT be any GitHub repository permissions assigned to individual users.

The names for the GitHub teams for each approved module are already defined in the respective Module Indexes. These teams MUST be created (and used) for each module.

The @Azure prefix in the last column of the tables linked above represents the “Azure” GitHub organization all AVM-related repositories exist in. DO NOT include this segment in the team’s name!

Non-FTE / external contributors (subject matter experts that aren’t Microsoft employees) can’t be members of the teams described in this chapter, hence, they won’t gain any extra permissions on AVM repositories, therefore, they need to work in forks.

Naming Convention

The naming convention for the GitHub teams MUST follow the below pattern:

  • <hyphenated module name>-module-owners-<bicep/tf> - to be assigned as the GitHub repository’s Module Owners team
  • <hyphenated module name>-module-contributors-<bicep/tf> - to be assigned as the GitHub repository’s Module Contributors team
The naming convention for Bicep modules is slightly different than the naming convention for their respective GitHub teams.

Segments:

  • <hyphenated module name> == the AVM Module’s name, with each segment separated by dashes, i.e., avm-res-<resource provider>-<ARM resource type>
    • See RMNFR1 for AVM Resource Module Naming
    • See PMNFR1 for AVM Pattern Module Naming
  • module-owners or module-contributors == the role the GitHub Team is assigned to
  • <bicep/tf> == the language the module is written in

Examples:

  • avm-res-compute-virtualmachine-module-owners-bicep
  • avm-res-compute-virtualmachine-module-contributors-tf

Add Team Members

All officially documented module owner(s) MUST be added to the -module-owners- team. The -module-owners- team MUST NOT have any other members.

Any additional module contributors whom the module owner(s) agreed to work with MUST be added to the -module-contributors- team.

Unless explicitly requested and agreed, members of the AVM core team or any PG teams MUST NOT be added to the -module-owners- or -module-contributors- teams as permissions for them are granted through the teams described in SNFR9.


Grant Permissions - Bicep
Team memberships
In case of Bicep modules, permissions to the BRM repository (the repo of the Bicep Registry) are granted via assigning the -module-owners- and -module-contributors- teams to parent teams that already have the required level access configured. While it is the module owner’s responsibility to initiate the addition of their teams to the respective parents, only the AVM core team can approve this parent-child relationship.

Module owners MUST create their -module-owners- and -module-contributors- teams and as part of the provisioning process, they MUST request the addition of these teams to their respective parent teams (see the table below for details).

GitHub Team NameDescriptionPermissionsPermissions granted throughWhere to work?
<hyphenated module name>-module-owners-bicepAVM Bicep Module Owners - <module name>WriteAssignment to the avm-technical-reviewers-bicep parent team.Need to work in a fork.
<hyphenated module name>-module-contributors-bicepAVM Bicep Module Contributors - <module name>Triageavm-module-contributors-bicep parent team.Need to work in a fork.

Examples - GitHub teams required for the Bicep resource module of Azure Virtual Network (avm/res/network/virtual-network):

  • avm-res-network-virtualnetwork-module-owners-bicep –> assign to the avm-technical-reviewers-bicep parent team.
  • avm-res-network-virtualnetwork-module-contributors-bicep –> assign to the avm-module-contributors-bicep parent team.

Direct link to create a new GitHub team and assign it to its parent: Create new team

Fill in the values as follows:

  • Team name: Following the naming convention described above, use the value defined in the module indexes.
  • Description: Follow the guidance above (see the Description column in the table above).
  • Parent team: Follow the guidance above (see the Permissions granted through column in the table above).
  • Team visibility: Visible
  • Team notifications: Enabled
CODEOWNERS file

As part of the “initial Pull Request” (that publishes the first version of the module), module owners MUST add an entry to the CODEOWNERS file in the BRM repository (here).

Through this approach, the AVM core team will grant review permission to module owners as part of the standard PR review process.

Every CODEOWNERS entry (line) MUST include the following segments separated by a single whitespace character:

  • Path of the module, relative to the repo’s root, e.g.: /avm/res/network/virtual-network/
  • The -module-owners-team, with the @Azure/ prefix, e.g., @Azure/avm-res-network-virtualnetwork-module-owners-bicep
  • The GitHub team of the AVM core team, with the @Azure/ prefix, i.e., @Azure/avm-core-team-technical-bicep

Example - CODEOWNERS entry for the Bicep resource module of Azure Virtual Network (avm/res/network/virtual-network):

  • /avm/res/network/virtual-network/ @Azure/avm-res-network-virtualnetwork-module-owners-bicep @Azure/avm-core-team-technical-bicep

Grant Permissions - Terraform

Module owners MUST assign the -module-owners-and -module-contributors- teams the necessary permissions on their Terraform module repository per the guidance below.

GitHub Team NameDescriptionPermissionsPermissions granted throughWhere to work?
<module name>-module-owners-tfAVM Terraform Module Owners - <module name>AdminDirect assignment to repoModule owner can decide whether they want to work in a branch local to the repo or in a fork.
<module name>-module-contributors-tfAVM Terraform Module Contributors - <module name>WriteDirect assignment to repoNeed to work in a fork.

Direct link to create a new GitHub team: Create new team

Fill in the values as follows:

  • Team name: Following the naming convention described above, use the value defined in the module indexes.
  • Description: Follow the guidance above (see the Description column in the table above).
  • Parent team: Do not assign the team to any parent team.
  • Team visibility: Visible
  • Team notifications: Enabled



ID: SNFR9 - Category: Contribution/Support - AVM & PG Teams GitHub Repo Permissions

A module owner MUST make the following GitHub teams in the Azure GitHub organization admins on the GitHub repo of the module in question:

Bicep
These required GitHub teams are already associated to the BRM repository and have the required permissions.
Terraform

Module owners MUST assign these GitHub teams as admins on the GitHub repo of the module in question.

For detailed steps, please follow this guidance.




ID: SNFR10 - Category: Contribution/Support - MIT Licensing

A module MUST be published with the MIT License in the Azure GitHub organization.




ID: SNFR11 - Category: Contribution/Support - Issues Response Times

A module owner MUST respond to logged issues within 3 business days. See Module Support for more information.




ID: SNFR12 - Category: Contribution/Support - Versions Supported

Only the latest released version of a module MUST be supported.

For example, if an AVM Resource Module is used in an AVM Pattern Module that was working but now is not. The first step by the AVM Pattern Module owner should be to upgrade to the latest version of the AVM Resource Module test and then if not fixed, troubleshoot and fix forward from the that latest version of the AVM Resource Module onwards.

This avoids AVM Module owners from having to maintain multiple major release versions.




ID: SNFR23 - Category: Contribution/Support - GitHub Repo Labels

GitHub repositories where modules are held MUST use the below labels and SHOULD not use any additional labels:

These labels are available in a CSV file from here

NameDescriptionHEX
AZD 🧑‍💻These modules are requested/used by the AZD team.
E0BFFA
Needs: Attention 👋Reply has been added to issue, maintainer to review
E99695
Needs: Immediate Attention ‼️Immediate attention of module owner / AVM team is needed
FF0000
Needs: Author Feedback 👂Awaiting feedback from the issue/PR author
F18A07
Needs: External Changes ⚒️When an issue/PR requires changes that are outside of the control of the module. e.g. to an RP.
DE389D
Needs: More Evidence ⚖We are looking for more evidence to make a decision on this
F64872
Needs: Triage 🔍Maintainers need to triage still
FBCA04
Needs: Module Owner 📣This module needs an owner to develop or maintain it
FF0019
Status: Awaiting Release To Be Cut ✂️This is fixed in the main branch but not in the latest release, will be fixed with next release cut
800080
Status: Do Not Merge ⛔Do not merge PRs with this label attached as they are not ready or aligned to future direction etc.
8B4513
Status: External Contribution 🌍This is being worked on by someone outside of the AVM module owners/contributors or AVM core team
D8FA2C
Status: Fixed ✅Auto label applied when issue fixed by merged PR
90EE90
Status: Help Wanted 🆘Extra attention is needed
FF4500
Status: In Triage 🔍Picked up for triaging by an AVM core team member
D4AF37
Status: In PR 👉This is when an issue is due to be fixed in an open PR
EDEDED
Status: Invalid ❌This doesn't seem right
E4E669
Status: Long Term ⏳We will do it, but will take a longer amount of time due to complexity/priorities
B60205
Status: No Recent Activity 💤When an issue/PR has not been modified for X amount of days
808080
Status: Won't Fix 💔This will not be worked on
FFFFFF
Status: Owners Identified 🤘This module has its owners identified
FBEF2A
Status: Module Available 🟢The module is published
C8E6C9
Status: Module Orphaned 👀The module has no owner and is therefore orphaned at this time
F4A460
Status: Response Overdue 🚩When an issue/PR has not been responded to for X amount of days
850000
Status: Looking For Assistance 🦆This item is looking for anyone to help develop the code and submit a PR for resolution
03FCC2
Type: Bug 🐛Something isn't working
D73A4A
Type: Documentation 📄Improvements or additions to documentation
0075CA
Type: Duplicate 🤲This issue or pull request already exists
CFD3D7
Type: Feature Request ➕New feature or request
A2EEEF
Type: Hygiene 🧹things related to testing, issue triage etc.
17016A
Type: New Module Proposal 💡A new module for AVM is being proposed
ADD8E6
Type: Question/Feedback 🙋‍♀️Further information is requested or just some feedback
CB6BA2
Type: Security Bug 🔒This is a security bug
FFFF00
Type: AVM 🅰️ ✌️ ⓜ️This is an AVM related issue
F0FFFF
Language: Terraform 🌐This is related to the Terraform IaC language
7740B6
Language: Bicep 💪This is related to the Bicep IaC language
1D73B3
Class: Resource Module 📦This is a resource module
D3D3D3
Class: Pattern Module 📦This is a pattern module
A9A9A9

To help apply these to a module GitHub repository you can use the below PowerShell script:

For most scenario this is the command you’ll need to call the below PowerShell script with, replacing the value for RepositoryName:

Set-AvmGitHubLabels.ps1 -RepositoryName "Org/MyGitHubRepo" -CreateCsvLabelExports $false -NoUserPrompts $true
# Linux / MacOs
# For Windows replace $PWD with your the local path or your repository
#
docker run -it -v $PWD:/repo -w /repo mcr.microsoft.com/powershell pwsh -Command '
    #Invoke-WebRequest -Uri "https://azure.github.io/Azure-Verified-Modules/scripts/Set-AvmGitHubLabels.ps1" -OutFile "Set-AvmGitHubLabels.ps1"
    $gh_version = "2.44.1"
    Invoke-WebRequest -Uri "https://github.com/cli/cli/releases/download/v2.44.1/gh_2.44.1_linux_amd64.tar.gz" -OutFile "gh_$($gh_version)_linux_amd64.tar.gz"
    apt-get update && apt-get install -y git
    tar -xzf "gh_$($gh_version)_linux_amd64.tar.gz"
    ls -lsa
    mv "gh_$($gh_version)_linux_amd64/bin/gh" /usr/local/bin/
    rm "gh_$($gh_version)_linux_amd64.tar.gz" && rm -rf "gh_$($gh_version)_linux_amd64"
    gh --version
    ls -lsa
    gh auth login
    $OrgProject = "Azure/terraform-azurerm-avm-res-kusto-cluster"
    gh auth status
    ./Set-AvmGitHubLabels.ps1 -RepositoryName $OrgProject -CreateCsvLabelExports $false -NoUserPrompts $true
  '

By default this script will only update and append labels on the repository specified. However, this can be changed by setting the parameter -UpdateAndAddLabelsOnly to $false, which will remove all the labels from the repository first and then apply the AVM labels from the CSV only.

Make sure you elevate your privilege to admin level or the labels will not be applied to your repository. Go to repos.opensource.microsoft.com/orgs/Azure/repos/ to request admin access before running the script.

Full Script:

These Set-AvmGitHubLabels.ps1 can be downloaded from here.

[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Justification = "Coloured output required in this script")]

<#
.SYNOPSIS
  This script can be used to create the Azure Verified Modules (AVM) standard GitHub labels to a GitHub repository.

.DESCRIPTION
  This script can be used to create the Azure Verified Modules (AVM) standard GitHub labels to a GitHub repository.

  By default, the script will remove all pre-existing labels and apply the AVM labels. However, this can be changed by using the -RemoveExistingLabels parameter and setting it to $false. The tool will also output the labels that exist in the repository before and after the script has run to a CSV file in the current directory, or a directory specified by the -OutputDirectory parameter.

  The AVM labels to be created are documented here: TBC

.NOTES
  Please ensure you have specified the GitHub repositry correctly. The script will prompt you to confirm the repository name before proceeding.

.COMPONENT
  You must have the GitHub CLI installed and be authenticated to a GitHub account with access to the repository you are applying the labels to before running this script.

.LINK
  TBC

.Parameter RepositoryName
  The name of the GitHub repository to apply the labels to.

.Parameter RemoveExistingLabels
  If set to $true, the default value, the script will remove all pre-existing labels from the repository specified in -RepositoryName before applying the AVM labels. If set to $false, the script will not remove any pre-existing labels.

.Parameter UpdateAndAddLabelsOnly
  If set to $true, the default value, the script will only update and add labels to the repository specified in -RepositoryName. If set to $false, the script will remove all pre-existing labels from the repository specified in -RepositoryName before applying the AVM labels.

.Parameter OutputDirectory
  The directory to output the pre-existing and post-existing labels to in a CSV file. The default value is the current directory.

.Parameter CreateCsvLabelExports
  If set to $true, the default value, the script will output the pre-existing and post-existing labels to a CSV file in the current directory, or a directory specified by the -OutputDirectory parameter. If set to $false, the script will not output the pre-existing and post-existing labels to a CSV file.

.Parameter GitHubCliLimit
  The maximum number of labels to return from the GitHub CLI. The default value is 999.

.Parameter LabelsToApplyCsvUri
  The URI to the CSV file containing the labels to apply to the GitHub repository. The default value is https://raw.githubusercontent.com/jtracey93/label-source/main/avm-github-labels.csv.

.Parameter NoUserPrompts
  If set to $true, the default value, the script will not prompt the user to confirm they want to remove all pre-existing labels from the repository specified in -RepositoryName before applying the AVM labels. If set to $false, the script will prompt the user to confirm they want to remove all pre-existing labels from the repository specified in -RepositoryName before applying the AVM labels.

  This is useful for running the script in automation workflows

.EXAMPLE
  Create the AVM labels in the repository Org/MyGitHubRepo and remove all pre-existing labels.

  Set-AvmGitHubLabels.ps1 -RepositoryName "Org/MyGitHubRepo"

.EXAMPLE
  Create the AVM labels in the repository Org/MyGitHubRepo and do not remove any pre-existing labels, just overwrite any labels that have the same name.

  Set-AvmGitHubLabels.ps1 -RepositoryName "Org/MyGitHubRepo" -RemoveExistingLabels $false

.EXAMPLE
  Create the AVM labels in the repository Org/MyGitHubRepo and output the pre-existing and post-existing labels to the directory C:\GitHubLabels.

  Set-AvmGitHubLabels.ps1 -RepositoryName "Org/MyGitHubRepo" -OutputDirectory "C:\GitHubLabels"

.EXAMPLE
  Create the AVM labels in the repository Org/MyGitHubRepo and output the pre-existing and post-existing labels to the directory C:\GitHubLabels and do not remove any pre-existing labels, just overwrite any labels that have the same name.

  Set-AvmGitHubLabels.ps1 -RepositoryName "Org/MyGitHubRepo" -OutputDirectory "C:\GitHubLabels" -RemoveExistingLabels $false

.EXAMPLE
  Create the AVM labels in the repository Org/MyGitHubRepo and do not create the pre-existing and post-existing labels CSV files and do not remove any pre-existing labels, just overwrite any labels that have the same name.

  Set-AvmGitHubLabels.ps1 -RepositoryName "Org/MyGitHubRepo" -RemoveExistingLabels $false -CreateCsvLabelExports $false

.EXAMPLE
  Create the AVM labels in the repository Org/MyGitHubRepo and do not create the pre-existing and post-existing labels CSV files and do not remove any pre-existing labels, just overwrite any labels that have the same name. Finally, use a custom CSV file hosted on the internet to create the labels from.

  Set-AvmGitHubLabels.ps1 -RepositoryName "Org/MyGitHubRepo" -OutputDirectory "C:\GitHubLabels" -RemoveExistingLabels $false -CreateCsvLabelExports $false -LabelsToApplyCsvUri "https://example.com/csv/avm-github-labels.csv"

#>

#Requires -PSEdition Core

[CmdletBinding()]
param (
  [Parameter(Mandatory = $true)]
  [string]$RepositoryName,

  [Parameter(Mandatory = $false)]
  [bool]$RemoveExistingLabels = $true,

  [Parameter(Mandatory = $false)]
  [bool]$UpdateAndAddLabelsOnly = $true,

  [Parameter(Mandatory = $false)]
  [bool]$CreateCsvLabelExports = $true,

  [Parameter(Mandatory = $false)]
  [string]$OutputDirectory = (Get-Location),

  [Parameter(Mandatory = $false)]
  [int]$GitHubCliLimit = 999,

  [Parameter(Mandatory = $false)]
  [string]$LabelsToApplyCsvUri = "https://azure.github.io/Azure-Verified-Modules/governance/avm-standard-github-labels.csv",

  [Parameter(Mandatory = $false)]
  [bool]$NoUserPrompts = $false
)

# Check if the GitHub CLI is installed
$GitHubCliInstalled = Get-Command gh -ErrorAction SilentlyContinue
if ($null -eq $GitHubCliInstalled) {
  throw "The GitHub CLI is not installed. Please install the GitHub CLI and try again."
}
Write-Host "The GitHub CLI is installed..." -ForegroundColor Green

# Check if GitHub CLI is authenticated
$GitHubCliAuthenticated = gh auth status
if ($LASTEXITCODE -ne 0) {
  Write-Host $GitHubCliAuthenticated -ForegroundColor Red
  throw "Not authenticated to GitHub. Please authenticate to GitHub using the GitHub CLI, `gh auth login`, and try again."
}
Write-Host "Authenticated to GitHub..." -ForegroundColor Green

# Check if GitHub repository name is valid
$GitHubRepositoryNameValid = $RepositoryName -match "^[a-zA-Z0-9-]+/[a-zA-Z0-9-]+$"
if ($false -eq $GitHubRepositoryNameValid) {
  throw "The GitHub repository name $RepositoryName is not valid. Please check the repository name and try again. The format must be <OrgName>/<RepoName>"
}

# List GitHub repository provided and check it exists
$GitHubRepository = gh repo view $RepositoryName
if ($LASTEXITCODE -ne 0) {
  Write-Host $GitHubRepository -ForegroundColor Red
  throw "The GitHub repository $RepositoryName does not exist. Please check the repository name and try again."
}
Write-Host "The GitHub repository $RepositoryName exists..." -ForegroundColor Green

# PRE - Get the current GitHub repository labels and export to a CSV file in the current directory or where -OutputDirectory specifies if set to a valid directory path and the directory exists or can be created if it does not exist already
if ($RemoveExistingLabels -or $UpdateAndAddLabelsOnly) {
  Write-Host "Getting the current GitHub repository (pre) labels for $RepositoryName..." -ForegroundColor Yellow
  $GitHubRepositoryLabels = gh label list -R $RepositoryName -L $GitHubCliLimit --json name,description,color

  if ($null -ne $GitHubRepositoryLabels -and $CreateCsvLabelExports -eq $true) {
    $csvFileNamePathPre = "$OutputDirectory\$($RepositoryName.Replace('/', '_'))-Labels-Pre-$(Get-Date -Format FileDateTime).csv"
    Write-Host "Exporting the current GitHub repository (pre) labels for $RepositoryName to $csvFileNamePathPre" -ForegroundColor Yellow
    $GitHubRepositoryLabels | ConvertFrom-Json | Export-Csv -Path $csvFileNamePathPre -NoTypeInformation
  }
}

# Remove all pre-existing labels if -RemoveExistingLabels is set to $true and user confirms they want to remove all pre-existing labels
if ($null -ne $GitHubRepositoryLabels) {
  $GitHubRepositoryLabelsJson = $GitHubRepositoryLabels | ConvertFrom-Json
  if ($RemoveExistingLabels -eq $true -and $NoUserPrompts -eq $false -and $UpdateAndAddLabelsOnly -eq $false) {
    $RemoveExistingLabelsConfirmation = Read-Host "Are you sure you want to remove all $($GitHubRepositoryLabelsJson.Count) pre-existing labels from $($RepositoryName)? (Y/N)"
    if ($RemoveExistingLabelsConfirmation -eq "Y") {
      Write-Host "Removing all pre-existing labels from $RepositoryName..." -ForegroundColor Yellow
      $GitHubRepositoryLabels | ConvertFrom-Json | ForEach-Object {
        Write-Host "Removing label $($_.name) from $RepositoryName..." -ForegroundColor DarkRed
        gh label delete -R $RepositoryName $_.name --yes
      }
    }
  }
  if ($RemoveExistingLabels -eq $true -and $NoUserPrompts -eq $true -and $UpdateAndAddLabelsOnly -eq $false) {
    Write-Host "Removing all pre-existing labels from $RepositoryName..." -ForegroundColor Yellow
    $GitHubRepositoryLabels | ConvertFrom-Json | ForEach-Object {
      Write-Host "Removing label $($_.name) from $RepositoryName..." -ForegroundColor DarkRed
      gh label delete -R $RepositoryName $_.name --yes
    }
  }
}
if ($null -eq $GitHubRepositoryLabels) {
  Write-Host "No pre-existing labels to remove or not selected to be removed from $RepositoryName..." -ForegroundColor Magenta
}

# Check LabelsToApplyCsvUri is valid and contains a CSV content
Write-Host "Checking $LabelsToApplyCsvUri is valid..." -ForegroundColor Yellow
$LabelsToApplyCsvUriValid = $LabelsToApplyCsvUri -match "^https?://"
if ($false -eq $LabelsToApplyCsvUriValid) {
  throw "The LabelsToApplyCsvUri $LabelsToApplyCsvUri is not valid. Please check the URI and try again. The format must be a valid URI."
}
Write-Host "The LabelsToApplyCsvUri $LabelsToApplyCsvUri is valid..." -ForegroundColor Green

# Create AVM lables from the AVM labels CSV file stored on the web using the convertfrom-csv cmdlet
$avmLabelsCsv = Invoke-WebRequest -Uri $LabelsToApplyCsvUri | ConvertFrom-Csv

# Check if the AVM labels CSV file contains the following columns: Name, Description, HEX
$avmLabelsCsvColumns = $avmLabelsCsv | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
$avmLabelsCsvColumnsValid = $avmLabelsCsvColumns -contains "Name" -and $avmLabelsCsvColumns -contains "Description" -and $avmLabelsCsvColumns -contains "HEX"
if ($false -eq $avmLabelsCsvColumnsValid) {
  throw "The labels CSV file does not contain the required columns: Name, Description, HEX. Please check the CSV file and try again. It contains the following columns: $avmLabelsCsvColumns"
}
Write-Host "The labels CSV file contains the required columns: Name, Description, HEX" -ForegroundColor Green

# Create the AVM labels in the GitHub repository
Write-Host "Creating/Updating the $($avmLabelsCsv.Count) AVM labels in $RepositoryName..." -ForegroundColor Yellow
$avmLabelsCsv | ForEach-Object {
  if ($GitHubRepositoryLabelsJson.name -contains $_.name) {
    Write-Host "The label $($_.name) already exists in $RepositoryName. Updating the label to ensure description and color are consitent..." -ForegroundColor Magenta
    gh label create -R $RepositoryName "$($_.name)" -c $_.HEX -d $($_.Description) --force
  }
  else {
    Write-Host "The label $($_.name) does not exist in $RepositoryName. Creating label $($_.name) in $RepositoryName..." -ForegroundColor Cyan
    gh label create -R $RepositoryName "$($_.Name)" -c $_.HEX -d $($_.Description) --force
  }
}

# POST - Get the current GitHub repository labels and export to a CSV file in the current directory or where -OutputDirectory specifies if set to a valid directory path and the directory exists or can be created if it does not exist already
if ($CreateCsvLabelExports -eq $true) {
  Write-Host "Getting the current GitHub repository (post) labels for $RepositoryName..." -ForegroundColor Yellow
  $GitHubRepositoryLabels = gh label list -R $RepositoryName -L $GitHubCliLimit --json name,description,color

  if ($null -ne $GitHubRepositoryLabels) {
    $csvFileNamePathPre = "$OutputDirectory\$($RepositoryName.Replace('/', '_'))-Labels-Post-$(Get-Date -Format FileDateTime).csv"
    Write-Host "Exporting the current GitHub repository (post) labels for $RepositoryName to $csvFileNamePathPre" -ForegroundColor Yellow
    $GitHubRepositoryLabels | ConvertFrom-Json | Export-Csv -Path $csvFileNamePathPre -NoTypeInformation
  }
}

# If -RemoveExistingLabels is set to $true and user confirms they want to remove all pre-existing labels check that only the avm labels exist in the repository
if ($RemoveExistingLabels -eq $true -and ($RemoveExistingLabelsConfirmation -eq "Y" -or $NoUserPrompts -eq $true) -and $UpdateAndAddLabelsOnly -eq $false) {
  Write-Host "Checking that only the AVM labels exist in $RepositoryName..." -ForegroundColor Yellow
  $GitHubRepositoryLabels = gh label list -R $RepositoryName -L $GitHubCliLimit --json name,description,color
  $GitHubRepositoryLabels | ConvertFrom-Json | ForEach-Object {
    if ($avmLabelsCsv.Name -notcontains $_.name) {
      throw "The label $($_.name) exists in $RepositoryName but is not in the CSV file."
    }
  }
  Write-Host "Only the CSV labels exist in $RepositoryName..." -ForegroundColor Green
}

Write-Host "The CSV labels have been created/updated in $RepositoryName..." -ForegroundColor Green



ID: SNFR14 - Category: Inputs - Data Types

A module SHOULD use either: simple data types. e.g., string, int, bool.

OR

Complex data types (objects, arrays, maps) when the language-compliant schema is defined.




ID: SNFR22 - Category: Inputs - Parameters/Variables for Resource IDs

A module parameter/variable that requires a full Azure Resource ID as an input value, e.g. /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{keyVaultName}, MUST contain ResourceId/resource_id in its parameter/variable name to assist users in knowing what value to provide at a glance of the parameter/variable name.

Example for the property workspaceId for the Diagnostic Settings resource. In Bicep its parameter name should be workspaceResourceId and the variable name in Terraform should be workspace_resource_id.

workspaceId is not descriptive enough and is ambiguous as to which ID is required to be input.




ID: SNFR15 - Category: Documentation - Automatic Documentation Generation

README documentation MUST be automatically/programmatically generated. MUST include the sections as defined in the language specific requirements BCPNFR2, TFNFR2.




ID: SNFR16 - Category: Documentation - Examples/E2E

An examples/e2e directory MUST exist to provide named scenarios for module deployment.




ID: SNFR17 - Category: Release - Semantic Versioning

You cannot specify the patch version for Bicep modules in the public Bicep Registry, as this is automatically incremented by 1 each time a module is published. You can only set the major and minor versions.

See the Bicep Contribution Guide for more information.

Modules MUST use semantic versioning (aka semver) for their versions and releases in accordance with: Semantic Versioning 2.0.0

For example all modules should be released using a semantic version that matches this pattern: X.Y.Z

  • X == Major Version
  • Y == Minor Version
  • Z == Patch Version

Initially modules should be released as version 0.1.0 and incremented via Minor and Patch versions only until the module owner is happy the module has been “road tested” and is now stable enough for it’s first Major release of version 1.0.0.

Releasing as version 0.1.0 initially and only incrementing Minor and Patch versions allows the module owner to make breaking changes more easily and frequently as it’s still not an official Major/Stable release. 👍



ID: SNFR18 - Category: Release - Breaking Changes

A module SHOULD avoid breaking changes, e.g., deprecating inputs vs. removing.




ID: SNFR19 - Category: Publishing - Registries Targeted

Modules MUST be published to their respective language public registries.

See the language specific contribution guides for detailed guidance and sample code to use in AVM modules to achieve this requirement.




ID: SNFR21 - Category: Publishing - Cross Language Collaboration

When the module owners of the same Resource or Pattern AVM module are not the same individual or team for all languages, each languages team SHOULD collaborate with their sibling language team for the same module to ensure consistency where possible.





Resource Module Requirements

Listed below are both functional and non-functional requirements for AVM Resource Modules.


Functional Requirements (RMFR)

This section includes resource module level, functional requirements (RMFR) for Bicep an Terraform.


ID: RMFR1 - Category: Composition - Single Resource Only

A resource module MUST only deploy a single instance of the primary resource, e.g., one virtual machine per instance.

Multiple instances of the module MUST be used to scale out.




ID: RMFR2 - Category: Composition - No Resource Wrapper Modules

A resource module MUST add value by including additional features on top of the primary resource.




ID: RMFR3 - Category: Composition - Resource Groups

A resource module MUST NOT create a Resource Group for resources that require them.

In the case that a Resource Group is required, a module MUST have an input (scope or variable):

  • In Bicep the targetScope MUST be set to resourceGroup or not specified (which means default to resourceGroup scope)
  • In Terraform the variable MUST be called resource_group_name

Scopes will be covered further in the respective language specific specifications.




ID: RMFR4 - Category: Composition - AVM Consistent Feature & Extension Resources Value Add

Resource modules support the following optional features/extension resources, as specified, if supported by the primary resource. The top-level variable/parameter names MUST be:

Optional Features/Extension ResourcesBicep Parameter NameTerraform Variable NameMUST/SHOULD
Diagnostic SettingsdiagnosticSettingsdiagnostic_settingsMUST
Role AssignmentsroleAssignmentsrole_assignmentsMUST
Resource LockslocklockMUST
TagstagstagsMUST
Managed Identities (System / User Assigned)managedIdentitiesmanaged_identitiesMUST
Private EndpointsprivateEndpointsprivate_endpointsMUST
Customer Managed KeyscustomerManagedKeycustomer_managed_keyMUST
Azure Monitor AlertsalertsalertsSHOULD

Resource modules MUST NOT deploy required/dependent resources for the optional features/extension resources specified above. For example, for Diagnostic Settings the resource module MUST NOT deploy the Log Analytics Workspace, this is expected to be already in existence from the perspective of the resource module deployed via another method/module etc.

Please note that the implementation of Customer Managed Keys from an ARM API perspective is different across various RPs that implement Customer Managed Keys in their service. For that reason you may see differences between modules on how Customer Managed Keys are handled and implemented, but functionality will be as expected.

Module owners MAY choose to utilize cross repo dependencies for these “add-on” resources, or MAY chose to implement the code directly in their own repo/module. So long as the implementation and outputs are as per the specifications requirements, then this is acceptable.

Make sure to checkout the language specific specifications for more info on this:




ID: RMFR5 - Category: Composition - AVM Consistent Feature & Extension Resources Value Add Interfaces/Schemas

Resource modules MUST implement a common interface, e.g. the input’s data structures and properties within them (objects/arrays/dictionaries/maps), for the optional features/extension resources:

See:




ID: RMFR8 - Category: Composition - Dependency on child and other resources

A resource module MAY contain references to other resource modules, however MUST NOT contain references to non-AVM modules nor AVM pattern modules.

See BCPFR1 and TFFR1 for more information on this.




ID: RMFR6 - Category: Inputs - Parameter/Variable Naming

Parameters/variables that pertain to the primary resource MUST NOT use the resource type in the name.

e.g., use sku, vs. virtualMachineSku/virtualmachine_sku

Another example for where RPs contain some of their name within a property, leave the property unchanged. E.g. Key Vault has a property called keySize, it is fine to leave as this and not remove the key part from the property/parameter name.




ID: RMFR7 - Category: Outputs - Minimum Required Outputs

Module owners MUST output the following outputs as a minimum in their modules:

OutputBicep Output NameTerraform Output Name
Resource Namenamename
Resource IDresourceIdresource_id
System Assigned Managed Identity Principal ID (if supported by module)systemAssignedMIPrincipalIdsystem_assigned_mi_principal_id

Module owners MAY also have to provide additional outputs depending on the IaC language, please check the language specific specs:




Non-Functional Requirements (RMNFR)

This section includes resource module level, non-functional requirements (RMNFR) for Bicep an Terraform.


ID: RMNFR1 - Category: Naming - Module Naming

We will maintain a set of CSV files in the AVM Central Repo (Azure/Azure-Verified-Modules) with the correct singular names for all resource types to enable checks to utilize this list to ensure repos are named correctly. To see the formatted content of these CSV files with additional information, please visit the AVM Module Indexes page.

This will be updated quarterly, or ad-hoc as new RPs/ Resources are created and highlighted via a check failure.

Resource modules MUST follow the below naming conventions (all lower case):

Bicep Resource Module Naming
  • Naming convention: avm/res/<hyphenated resource provider name>/<hyphenated ARM resource type> (module name for registry)
  • Example: avm/res/compute/virtual-machine or avm/res/managed-identity/user-assigned-identity
  • Segments:
    • res defines this is a resource module
    • <hyphenated resource provider name> is the resource provider’s name after the Microsoft part, with each word starting with a capital letter separated by dashes, e.g., Microsoft.Compute = compute, Microsoft.ManagedIdentity = managed-identity.
    • <hyphenated ARM resource type> is the singular version of the word after the resource provider, with each word starting with a capital letter separated by dashes, e.g., Microsoft.Compute/virtualMachines = virtual-machine, BUT Microsoft.Network/trafficmanagerprofiles = trafficmanagerprofile - since trafficmanagerprofiles is all lower case as per the ARM API definition.
Terraform Resource Module Naming
  • Naming convention:
    • avm-res-<resource provider>-<ARM resource type> (module name for registry)
    • terraform-<provider>-avm-res-<resource provider>-<ARM resource type> (GitHub repository name to meet registry naming requirements)
  • Example: avm-res-compute-virtualmachine or avm-res-managedidentity-userassignedidentity
  • Segments:
    • <provider> is the logical abstraction of various APIs used by Terraform. In most cases, this is going to be azurerm or azuread for resource modules.
    • res defines this is a resource module
    • <resource provider> is the resource provider’s name after the Microsoft part, e.g., Microsoft.Compute = compute.
    • <ARM resource type> is the singular version of the word after the resource provider, e.g., Microsoft.Compute/virtualMachines = virtualmachine



ID: RMNFR2 - Category: Inputs - Parameter/Variable Naming

A resource module MUST use the following standard inputs:

  • name (no default)
  • location (if supported by the resource and not a global resource, then use Resource Group location, if resource supports Resource Groups, otherwise no default)



ID: RMNFR3 - Category: Composition - RP Collaboration

Module owners (Microsoft FTEs) SHOULD reach out to the respective Resource Provider teams to build a partnership and collaboration on the modules creation, existence and long term maintenance.

Review this wiki page (Microsoft Internal) for more information.





Pattern Module Requirements

Listed below are both functional and non-functional requirements for AVM Pattern Modules.


Functional Requirements (PMFR)

This section includes pattern module level, functional requirements (PMFR) for Bicep an Terraform.


ID: PMFR1 - Category: Composition - Resource Group Creation

A Pattern Module MAY create Resource Group(s).




Non-Functional Requirements (PMNFR)

This section includes pattern module level, non-functional requirements (PMNFR) for Bicep an Terraform.


ID: PMNFR1 - Category: Naming - Module Naming

Pattern Modules MUST follow the below naming conventions (all lower case):

Bicep Pattern Module Naming
  • Naming convention: avm/ptn/<hyphenated grouping/category name>/<hyphenated pattern module name>
  • Example: avm/ptn/compute/app-tier-vmss or avm/ptn/avd-lza/management-plane or avm/ptn/3-tier/web-app
  • Segments:
    • ptn defines this as a pattern module
    • <hyphenated grouping/category name> is a hierarchical grouping of pattern modules by category, with each word separated by dashes, such as:
      • project name, e.g., avd-lza,
      • primary resource provider, e.g., compute or network, or
      • architecture, e.g., 3-tier
    • <hyphenated pattern module name> is a term describing the module’s function, with each word separated by dashes, e.g., app-tier-vmss = Application Tier VMSS; management-plane = Azure Virtual Desktop Landing Zone Accelerator Management Plane
Terraform Pattern Module Naming
  • Naming convention:
    • avm-ptn-<pattern module name> (Module name for registry)
    • terraform-<provider>-avm-ptn-<pattern module name> (GitHub repository name to meet registry naming requirements)
  • Example: avm-ptn-apptiervmss or avm-ptn-avd-lza-managementplane
  • Segments:
    • <provider> is the logical abstraction of various APIs used by Terraform. In most cases, this is going to be azurerm or azuread for resource modules.
    • ptn defines this as a pattern module
    • <pattern module name> is a term describing the module’s function, e.g., apptiervmss = Application Tier VMSS; avd-lza-managementplane = Azure Virtual Desktop Landing Zone Accelerator Management Plane



ID: PMNFR2 - Category: Composition - Use Resource Modules to Build a Pattern Module

A Pattern Module SHOULD be built from AVM Resources Modules to establish a standardized code base and improve maintainability. If a valid reason exists, a pattern module MAY contain native resources (“vanilla” code) where it’s necessary. A Pattern Module MUST NOT contain references to non-AVM modules.

Valid reasons for not using a Resource Module for a resource required by a Pattern Module include but are not limited to:

  • When using a Resource Module would result in hitting scaling limitations and/or would reduce the capabilities of the Pattern Module due to the limitations of Azure Resource Manager.
  • Developing a Pattern Module under time constraint, without having all required Resource Modules readily available.
In the latter case, the Pattern Module SHOULD be updated to use the Resource Module when the required Resource Module becomes available, to avoid accumulating technical debt. Ideally, all required Resource Modules SHOULD be developed first, and then leveraged by the Pattern Module.



ID: PMNFR3 - Category: Composition - Use other Pattern Modules to Build a Pattern Module

A Pattern Module MAY contain and be built using other AVM Pattern Modules. A Pattern Module MUST NOT contain references to non-AVM modules.




ID: PMNFR4 - Category: Hygiene - Missing Resource Module(s)

An item MUST be logged onto as an issue on the AVM Central Repo (Azure/Azure-Verified-Modules) if a Resource Module does not exist for resources deployed by the pattern module.

If the Resource Module adds no value, see Resource Module functional requirement ID: RMFR2.



ID: PMNFR5 - Category: Inputs - Parameter/Variable Naming

Parameter/variable input names SHOULD contain the resource to which they pertain. E.g., virtualMachineSku/virtualmachine_sku