Azure Verified Modules

New content: AI-Assisted IaC Solution Development

New articles available on specification-driven development with AVM modules! Learn how to use GitHub Copilot and Spec Kit to accelerate the development of Azure infrastructure solutions with AVM. See end-to-end examples for both Bicep and Terraform.

Introduction

Value Proposition

Azure Verified Modules (AVM) is an initiative to consolidate and set the standards for what a good Infrastructure-as-Code module looks like.

Modules will then align to these standards, across languages (Bicep, Terraform etc.) and will then be classified as AVMs and available from their respective language specific registries.

AVM is a common code base, a toolkit for our Customers, our Partners, and Microsoft. It’s an official, Microsoft driven initiative, with a devolved ownership approach to develop modules, leveraging internal & external communities.

Azure Verified Modules enable and accelerate consistent solution development and delivery of cloud-native or migrated applications and their supporting infrastructure by codifying Microsoft guidance (WAF), with best practice configurations.

AVM development cycle

Modules

AVM module classificationsAzure Verified Modules provides two types of modules: Resource and Pattern modules.

AVM modules are used to deploy Azure resources and their extensions, as well as reusable architectural patterns consistently.

Modules are composable building blocks that encapsulate groups of resources dedicated to one task.

  • Flexible, generalized, multi-purpose
  • Integrates child resources
  • Integrates extension resources

AVM improves code quality and provides a unified customer experience.

Important

AVM is owned, developed & supported by Microsoft, you may raise a GitHub issue on this repository or the module’s repository directly to get support or log feature requests.

You can also log a support ticket and if the issue is not related to the Azure platform, you will be redirected to submit a GitHub issue for the module owner(s) or the AVM team.

See Module Support for more information.

Next Steps

  1. Review Overview

  2. Review the Module Classification Definitions

  3. Review the Specifications

  4. Review the FAQ

  5. Learn how to contribute to AVM

AVM AVM

Subsections of Azure Verified Modules

Subsections of Overview

Introduction

What is Azure Verified Modules?

Azure Verified Modules (AVM), as “One Microsoft”, we want to provide and define the single definition of what a good IaC module is;

  • How they should be constructed and built
    • Enforcing consistency and testing where possible
  • How they are to be consumed
  • What they deliver for consumers in terms of resources deployed and configured
  • And where appropriate aligned across IaC languages (e.g. Bicep, Terraform, etc.).
Mission Statement

Our mission is to deliver a comprehensive Azure Verified Modules library in multiple IaC languages, following the principles of the well-architected framework, serving as the trusted Microsoft source of truth. Supported by Microsoft, AVM will accelerate deployment time for Azure resources and architectural patterns, empowering every person and organization on the planet on their IaC journey.

Definition of “Verified” Summary

  • The modules are supported by Microsoft, across it’s many internal organizations, as described in Module Support
  • Modules are aligned to clear specifications that enforces consistency between all AVM modules. See the ‘Specifications & Definitions’ section in the menu
  • Modules will continue to stay up-to-date with product/service roadmaps owned by the module owners and contributors
  • Modules will align to WAF high priority recommendations. See ‘What does AVM mean by “WAF Aligned”?’
  • Modules will provide clear documentation alongside examples to promote self-service consumption
  • Modules will be tested to ensure they comply with the specifications for AVM and their examples deploy as intended

Why Azure Verified Modules?

This effort to create Azure Verified Modules, with a strategy and definition, is required based on the sheer number of existing attempts from all areas across Microsoft to try and address this same area for our customers and partners. Across Microsoft there are many initiatives, projects and repositories that host and provide IaC modules in several languages, for example Bicep and Terraform. Each of these come with differing code styling and standards, consumption methods and approaches, testing frameworks, target personas, contribution guidelines, module definitions and most importantly support statements from their owners and maintainers.

However, none of these existing attempts have ever made it all the way through to becoming a brand and the go to place for IaC modules from Microsoft that consumers can trust (mainly around longevity and support), build upon and contribute back to.

Performing this effort now to create a shared single aligned strategy and definition for IaC modules from Microsoft, as One Microsoft, will allow us to accelerate existing and future projects, such as Application Landing Zone Accelerators (LZAs), as well as providing the building blocks via a library of modules, in the language of the consumers choice, that is consistent, trusted and supported by Microsoft. This all leads to consumers being able to accelerate faster, no matter what stage of their IaC journey they are on.

We also know, from our customers, that well defined support statements from Microsoft are required for initiatives like this to succeed at scale, especially in larger enterprise customers. We have seen over the past FY that this topic alone is important and is one that has led to confusion and frustration to customers who are consuming modules developed by individuals that in the end are not “officially” Microsoft supported and this unfortunately normally occurs at a critical point in time for the project being worked on, which amplifies frustrations.

How will we create, support and enforce Azure Verified Modules?

Azure Verified Modules will achieve this, and its mission statement, by implementing and enforcing the following; driven by the AVM Core Team:

  1. Publishing AVM modules to their respective public registries for consumption
  2. Creating, publishing and maintaining the Azure Verified Modules specifications (this site)
    • Including IaC language specific specifications (today Bicep and Terraform)
  3. Creating easy to follow AVM module contribution and publishing guidance for each IaC language (today Bicep and Terraform)
  4. Enforcing tests for each AVM module is compliant with the AVM specifications, where possible, via Unit and Integration tests
  5. Enforcing End-to-End Deployment tests of each AVM module
  6. Providing, and backing, a long-term support statement, regardless of the AVM module’s ownership status
    • Backed by the AVM Core Team, Microsoft CSS and Azure PGs

Module Indexes

Summary

The following table shows the number of all available, orphaned and planned AVM Bicep and Terraform Modules.

LanguageClassificationPublished 🟢 & 🟡Proposed ⚪SUM
BicepResource17023193
Pattern412061
Utility112
TerraformResource11141152
Pattern263056
Utility11314
➕ Additional information
Legend

Summary of status icons used on this page

IconStatusDescription
Proposed modulesModules that are proposed and/or being worked on but not published yet.
🟢 & 🟡Published modulesAvailable (🟢) and Orphaned (🟡) modules that are active and usable.
🔴Deprecated modulesModules that reached the end of their lifecycle.
📇All modulesIncluding Published, Proposed and Deprecated ones.

See the Module Lifecycle page for more details.

Want to contribute to AVM modules?
#
Labels
Link and description
1.Type: New Module Proposal 💡
Needs: Module Owner 📣
To become the owner of a new module, see all new modules looking for owners or check out the “Looking for owners” swimlane here.
2.Status: Module Orphaned 🟡To become the owner of an orphaned module, see all orphaned modules or check out the “Orphaned” swimlane here.
3.Needs: Module Contributor 📣To become a co-owner or contribute to a module, see all modules looking for contributors.

For more details on “What are the different ways to contribute to AVM?”, see here.

Subsections of Module Indexes

Bicep Modules

Summary

The following table shows the number of all available, orphaned and planned Bicep Modules.

LanguageClassificationPublished 🟢 & 🟡Proposed ⚪SUM
BicepResource17023193
Pattern412061
Utility112
➕ Additional information
Legend

Summary of status icons used on this page

IconStatusDescription
Proposed modulesModules that are proposed and/or being worked on but not published yet.
🟢 & 🟡Published modulesAvailable (🟢) and Orphaned (🟡) modules that are active and usable.
🔴Deprecated modulesModules that reached the end of their lifecycle.
📇All modulesIncluding Published, Proposed and Deprecated ones.

See the Module Lifecycle page for more details.

Want to contribute to AVM Bicep modules?
#LabelsLink and description
1.Type: New Module Proposal 💡
Needs: Module Owner 📣
Language: Bicep 💪
To become the owner of a new Bicep module, see all new Bicep modules looking for owners or check out the “Looking for owners” swimlane here.
2.Status: Module Orphaned 🟡 Language: Bicep 💪To become the owner of an orphaned Bicep module, see all orphaned Bicep modules or check out the “Orphaned” swimlane here.
3.Needs: Module Contributor 📣 Language: Bicep 💪To become a co-owner or contribute to a Bicep module, see all Bicep modules looking for contributors.

For more details on “What are the different ways to contribute to AVM?”, see here.

Status Badges

This section gives you an overview of the latest workflow status of each AVM module in the Public Bicep Registry repository.

Note

While some pipelines can momentarily show as red, a new module version cannot be published without a successful test run. A failing test may indicate a recent change to the platform that is causing a break in the module or any intermittent errors, such as a periodic test deployment attempting to create a resource with a name already taken in another Azure region.

#ModuleStatus
1ptn/aca-lza

hosting-environment

avm.ptn.aca-lza.hosting-environment avm.ptn.aca-lza.hosting-environment
2ptn/ai-ml

ai-foundry

avm.ptn.ai-ml.ai-foundry avm.ptn.ai-ml.ai-foundry
3ptn/ai-platform

baseline

avm.ptn.ai-platform.baseline avm.ptn.ai-platform.baseline
4ptn/alz

ama

avm.ptn.alz.ama avm.ptn.alz.ama
5ptn/alz

empty

avm.ptn.alz.empty avm.ptn.alz.empty
6ptn/app-service-lza

hosting-environment

avm.ptn.app-service-lza.hosting-environment avm.ptn.app-service-lza.hosting-environment
7ptn/app

container-job-toolkit

avm.ptn.app.container-job-toolkit.yml avm.ptn.app.container-job-toolkit.yml
8ptn/app

iaas-vm-cosmosdb-tier4

avm.ptn.app.iaas-vm-cosmosdb-tier4 avm.ptn.app.iaas-vm-cosmosdb-tier4
9ptn/app

paas-ase-cosmosdb-tier4

avm.ptn.app.paas-ase-cosmosdb-tier4 avm.ptn.app.paas-ase-cosmosdb-tier4
10ptn/authorization

pim-role-assignment

avm.ptn.authorization.pim-role-assignment avm.ptn.authorization.pim-role-assignment
11ptn/authorization

policy-assignment

avm.ptn.authorization.policy-assignment avm.ptn.authorization.policy-assignment
12ptn/authorization

policy-exemption

avm.ptn.authorization.policy-exemption avm.ptn.authorization.policy-exemption
13ptn/authorization

resource-role-assignment

avm.ptn.authorization.resource-role-assignment avm.ptn.authorization.resource-role-assignment
14ptn/authorization

role-assignment

avm.ptn.authorization.role-assignment avm.ptn.authorization.role-assignment
15ptn/authorization

role-definition

avm.ptn.authorization.role-definition avm.ptn.authorization.role-definition
16ptn/azd

acr-container-app

avm.ptn.azd.acr-container-app avm.ptn.azd.acr-container-app
17ptn/azd

aks

avm.ptn.azd.aks avm.ptn.azd.aks
18ptn/azd

aks-automatic-cluster

avm.ptn.azd.aks-automatic-cluster avm.ptn.azd.aks-automatic-cluster
19ptn/azd

apim-api

avm.ptn.azd.apim-api avm.ptn.azd.apim-api
20ptn/azd

container-app-upsert

avm.ptn.azd.container-app-upsert avm.ptn.azd.container-app-upsert
21ptn/azd

container-apps-stack

avm.ptn.azd.container-apps-stack avm.ptn.azd.container-apps-stack
22ptn/azd

insights-dashboard

avm.ptn.azd.insights-dashboard avm.ptn.azd.insights-dashboard
23ptn/azd

ml-ai-environment

Deprecated
24ptn/azd

ml-hub-dependencies

Deprecated
25ptn/azd

ml-project

Deprecated
26ptn/azd

monitoring

avm.ptn.azd.monitoring avm.ptn.azd.monitoring
27ptn/data

private-analytical-workspace

avm.ptn.data.private-analytical-workspace avm.ptn.data.private-analytical-workspace
28ptn/deployment-script

import-image-to-acr

avm.ptn.deployment-script.import-image-to-acr avm.ptn.deployment-script.import-image-to-acr
29ptn/dev-ops

cicd-agents-and-runners

avm.ptn.dev-ops.cicd-agents-and-runners avm.ptn.dev-ops.cicd-agents-and-runners
30ptn/finops-toolkit

finops-hub

avm.ptn.finops-toolkit.finops-hub avm.ptn.finops-toolkit.finops-hub
31ptn/lz

sub-vending

avm.ptn.lz.sub-vending avm.ptn.lz.sub-vending
32ptn/mgmt-groups

subscription-placement

avm.ptn.mgmt-groups.subscription-placement avm.ptn.mgmt-groups.subscription-placement
33ptn/network

hub-networking

avm.ptn.network.hub-networking avm.ptn.network.hub-networking
34ptn/network

private-link-private-dns-zones

avm.ptn.network.private-link-private-dns-zones avm.ptn.network.private-link-private-dns-zones
35ptn/network

virtual-wan

avm.ptn.network.virtual-wan avm.ptn.network.virtual-wan
36ptn/policy-insights

remediation

avm.ptn.policy-insights.remediation avm.ptn.policy-insights.remediation
37ptn/sa

build-your-own-copilot

Deprecated
38ptn/sa

chat-with-your-data

avm.ptn.sa.chat-with-your-data avm.ptn.sa.chat-with-your-data
39ptn/sa

content-generation

avm.ptn.sa.content-generation avm.ptn.sa.content-generation
40ptn/sa

content-processing

avm.ptn.sa.content-processing avm.ptn.sa.content-processing
41ptn/sa

conversation-knowledge-mining

avm.ptn.sa.conversation-knowledge-mining avm.ptn.sa.conversation-knowledge-mining
42ptn/sa

customer-chatbot

avm.ptn.sa.customer-chatbot avm.ptn.sa.customer-chatbot
43ptn/sa

document-knowledge-mining

avm.ptn.sa.document-knowledge-mining avm.ptn.sa.document-knowledge-mining
44ptn/sa

modernize-your-code

avm.ptn.sa.modernize-your-code avm.ptn.sa.modernize-your-code
45ptn/sa

multi-agent-custom-automation-engine

avm.ptn.sa.multi-agent-custom-automation-engine avm.ptn.sa.multi-agent-custom-automation-engine
46ptn/security

security-center

avm.ptn.security.security-center avm.ptn.security.security-center
47ptn/subscription

service-health-alerts

avm.ptn.subscription.service-health-alerts avm.ptn.subscription.service-health-alerts
48ptn/virtual-machine-images

azure-image-builder

avm.ptn.virtual-machine-images.azure-image-builder avm.ptn.virtual-machine-images.azure-image-builder
49res/aad

domain-service

avm.res.aad.domain-service avm.res.aad.domain-service
50res/alerts-management

action-rule

avm.res.alerts-management.action-rule avm.res.alerts-management.action-rule
51res/analysis-services

server

avm.res.analysis-services.server avm.res.analysis-services.server
52res/api-management

service

avm.res.api-management.service avm.res.api-management.service
53res/app-configuration

configuration-store

avm.res.app-configuration.configuration-store avm.res.app-configuration.configuration-store
54res/app

container-app

avm.res.app.container-app avm.res.app.container-app
55res/app

job

avm.res.app.job avm.res.app.job
56res/app

managed-environment

avm.res.app.managed-environment avm.res.app.managed-environment
57res/app

session-pool

avm.res.app.session-pool avm.res.app.session-pool
58res/authorization

policy-assignment

avm.res.authorization.policy-assignment avm.res.authorization.policy-assignment
59res/authorization

role-assignment

avm.res.authorization.role-assignment avm.res.authorization.role-assignment
60res/automation

automation-account

avm.res.automation.automation-account avm.res.automation.automation-account
61res/azure-stack-hci

cluster

avm.res.azure-stack-hci.cluster avm.res.azure-stack-hci.cluster
62res/azure-stack-hci

logical-network

avm.res.azure-stack-hci.logical-network avm.res.azure-stack-hci.logical-network
63res/azure-stack-hci

marketplace-gallery-image

avm.res.azure-stack-hci.marketplace-gallery-image avm.res.azure-stack-hci.marketplace-gallery-image
64res/azure-stack-hci

network-interface

avm.res.azure-stack-hci.network-interface avm.res.azure-stack-hci.network-interface
65res/azure-stack-hci

virtual-hard-disk

avm.res.azure-stack-hci.virtual-hard-disk avm.res.azure-stack-hci.virtual-hard-disk
66res/azure-stack-hci

virtual-machine-instance

avm.res.azure-stack-hci.virtual-machine-instance avm.res.azure-stack-hci.virtual-machine-instance
67res/batch

batch-account

avm.res.batch.batch-account avm.res.batch.batch-account
68res/cache

redis

avm.res.cache.redis avm.res.cache.redis
69res/cache

redis-enterprise

avm.res.cache.redis-enterprise avm.res.cache.redis-enterprise
70res/cdn

profile

avm.res.cdn.profile avm.res.cdn.profile
71res/cognitive-services

account

avm.res.cognitive-services.account avm.res.cognitive-services.account
72res/communication

communication-service

avm.res.communication.communication-service avm.res.communication.communication-service
73res/communication

email-service

avm.res.communication.email-service avm.res.communication.email-service
74res/compute

availability-set

avm.res.compute.availability-set avm.res.compute.availability-set
75res/compute

disk

avm.res.compute.disk avm.res.compute.disk
76res/compute

disk-encryption-set

avm.res.compute.disk-encryption-set avm.res.compute.disk-encryption-set
77res/compute

gallery

avm.res.compute.gallery avm.res.compute.gallery
78res/compute

image

avm.res.compute.image avm.res.compute.image
79res/compute

proximity-placement-group

avm.res.compute.proximity-placement-group avm.res.compute.proximity-placement-group
80res/compute

ssh-public-key

avm.res.compute.ssh-public-key avm.res.compute.ssh-public-key
81res/compute

virtual-machine

avm.res.compute.virtual-machine avm.res.compute.virtual-machine
82res/compute

virtual-machine-scale-set

avm.res.compute.virtual-machine-scale-set avm.res.compute.virtual-machine-scale-set
83res/consumption

budget

avm.res.consumption.budget avm.res.consumption.budget
84res/container-instance

container-group

avm.res.container-instance.container-group avm.res.container-instance.container-group
85res/container-registry

registry

avm.res.container-registry.registry avm.res.container-registry.registry
86res/container-service

managed-cluster

avm.res.container-service.managed-cluster avm.res.container-service.managed-cluster
87res/data-factory

factory

avm.res.data-factory.factory avm.res.data-factory.factory
88res/data-protection

backup-vault

avm.res.data-protection.backup-vault avm.res.data-protection.backup-vault
89res/databricks

access-connector

avm.res.databricks.access-connector avm.res.databricks.access-connector
90res/databricks

workspace

avm.res.databricks.workspace avm.res.databricks.workspace
91res/db-for-my-sql

flexible-server

avm.res.db-for-my-sql.flexible-server avm.res.db-for-my-sql.flexible-server
92res/db-for-postgre-sql

flexible-server

avm.res.db-for-postgre-sql.flexible-server avm.res.db-for-postgre-sql.flexible-server
93res/desktop-virtualization

application-group

avm.res.desktop-virtualization.application-group avm.res.desktop-virtualization.application-group
94res/desktop-virtualization

host-pool

avm.res.desktop-virtualization.host-pool avm.res.desktop-virtualization.host-pool
95res/desktop-virtualization

scaling-plan

avm.res.desktop-virtualization.scaling-plan avm.res.desktop-virtualization.scaling-plan
96res/desktop-virtualization

workspace

avm.res.desktop-virtualization.workspace avm.res.desktop-virtualization.workspace
97res/dev-center

devcenter

avm.res.dev-center.devcenter avm.res.dev-center.devcenter
98res/dev-center

network-connection

avm.res.dev-center.network-connection avm.res.dev-center.network-connection
99res/dev-center

project

avm.res.dev-center.project avm.res.dev-center.project
100res/dev-ops-infrastructure

pool

avm.res.dev-ops-infrastructure.pool avm.res.dev-ops-infrastructure.pool
101res/dev-test-lab

lab

avm.res.dev-test-lab.lab avm.res.dev-test-lab.lab
102res/devices

iot-hub

avm.res.devices.iot-hub avm.res.devices.iot-hub
103res/digital-twins

digital-twins-instance

avm.res.digital-twins.digital-twins-instance avm.res.digital-twins.digital-twins-instance
104res/document-db

database-account

avm.res.document-db.database-account avm.res.document-db.database-account
105res/document-db

mongo-cluster

avm.res.document-db.mongo-cluster avm.res.document-db.mongo-cluster
106res/elastic-san

elastic-san

avm.res.elastic-san.elastic-san avm.res.elastic-san.elastic-san
107res/event-grid

domain

avm.res.event-grid.domain avm.res.event-grid.domain
108res/event-grid

namespace

avm.res.event-grid.namespace avm.res.event-grid.namespace
109res/event-grid

system-topic

avm.res.event-grid.system-topic avm.res.event-grid.system-topic
110res/event-grid

topic

avm.res.event-grid.topic avm.res.event-grid.topic
111res/event-hub

namespace

avm.res.event-hub.namespace avm.res.event-hub.namespace
112res/fabric

capacity

avm.res.fabric.capacity avm.res.fabric.capacity
113res/health-bot

health-bot

avm.res.health-bot.health-bot avm.res.health-bot.health-bot
114res/healthcare-apis

workspace

avm.res.healthcare-apis.workspace avm.res.healthcare-apis.workspace
115res/hybrid-compute

gateway

avm.res.hybrid-compute.gateway avm.res.hybrid-compute.gateway
116res/hybrid-compute

license

avm.res.hybrid-compute.license avm.res.hybrid-compute.license
117res/hybrid-compute

machine

avm.res.hybrid-compute.machine avm.res.hybrid-compute.machine
118res/hybrid-container-service

provisioned-cluster-instance

avm.res.hybrid-container-service.provisioned-cluster-instance avm.res.hybrid-container-service.provisioned-cluster-instance
119res/insights

action-group

avm.res.insights.action-group avm.res.insights.action-group
120res/insights

activity-log-alert

avm.res.insights.activity-log-alert avm.res.insights.activity-log-alert
121res/insights

component

avm.res.insights.component avm.res.insights.component
122res/insights

data-collection-endpoint

avm.res.insights.data-collection-endpoint avm.res.insights.data-collection-endpoint
123res/insights

data-collection-rule

avm.res.insights.data-collection-rule avm.res.insights.data-collection-rule
124res/insights

diagnostic-setting

avm.res.insights.diagnostic-setting avm.res.insights.diagnostic-setting
125res/insights

metric-alert

avm.res.insights.metric-alert avm.res.insights.metric-alert
126res/insights

private-link-scope

avm.res.insights.private-link-scope avm.res.insights.private-link-scope
127res/insights

scheduled-query-rule

avm.res.insights.scheduled-query-rule avm.res.insights.scheduled-query-rule
128res/insights

webtest

avm.res.insights.webtest avm.res.insights.webtest
129res/key-vault

vault

avm.res.key-vault.vault avm.res.key-vault.vault
130res/kubernetes-configuration

extension

avm.res.kubernetes-configuration.extension avm.res.kubernetes-configuration.extension
131res/kubernetes-configuration

flux-configuration

avm.res.kubernetes-configuration.flux-configuration avm.res.kubernetes-configuration.flux-configuration
132res/kubernetes-runtime

load-balancer

avm.res.kubernetes-runtime.load-balancer avm.res.kubernetes-runtime.load-balancer
133res/kubernetes

connected-cluster

avm.res.kubernetes.connected-cluster avm.res.kubernetes.connected-cluster
134res/kusto

cluster

avm.res.kusto.cluster avm.res.kusto.cluster
135res/load-test-service

load-test

avm.res.load-test-service.load-test avm.res.load-test-service.load-test
136res/logic

integration-account

avm.res.logic.integration-account avm.res.logic.integration-account
137res/logic

workflow

avm.res.logic.workflow avm.res.logic.workflow
138res/machine-learning-services

registry

avm.res.machine-learning-services.registry avm.res.machine-learning-services.registry
139res/machine-learning-services

workspace

avm.res.machine-learning-services.workspace avm.res.machine-learning-services.workspace
140res/maintenance

configuration-assignment

avm.res.maintenance.configuration-assignment avm.res.maintenance.configuration-assignment
141res/maintenance

maintenance-configuration

avm.res.maintenance.maintenance-configuration avm.res.maintenance.maintenance-configuration
142res/managed-identity

user-assigned-identity

avm.res.managed-identity.user-assigned-identity avm.res.managed-identity.user-assigned-identity
143res/managed-services

registration-definition

avm.res.managed-services.registration-definition avm.res.managed-services.registration-definition
144res/management

management-group

avm.res.management.management-group avm.res.management.management-group
145res/management

service-group

avm.res.management.service-group avm.res.management.service-group
146res/maps

account

avm.res.maps.account avm.res.maps.account
147res/net-app

net-app-account

avm.res.net-app.net-app-account avm.res.net-app.net-app-account
148res/network

application-gateway

avm.res.network.application-gateway avm.res.network.application-gateway
149res/network

application-gateway-web-application-firewall-policy

avm.res.network.application-gateway-web-application-firewall-policy avm.res.network.application-gateway-web-application-firewall-policy
150res/network

application-security-group

avm.res.network.application-security-group avm.res.network.application-security-group
151res/network

azure-firewall

avm.res.network.azure-firewall avm.res.network.azure-firewall
152res/network

bastion-host

avm.res.network.bastion-host avm.res.network.bastion-host
153res/network

connection

avm.res.network.connection avm.res.network.connection
154res/network

ddos-protection-plan

avm.res.network.ddos-protection-plan avm.res.network.ddos-protection-plan
155res/network

dns-forwarding-ruleset

avm.res.network.dns-forwarding-ruleset avm.res.network.dns-forwarding-ruleset
156res/network

dns-resolver

avm.res.network.dns-resolver avm.res.network.dns-resolver
157res/network

dns-zone

avm.res.network.dns-zone avm.res.network.dns-zone
158res/network

express-route-circuit

avm.res.network.express-route-circuit avm.res.network.express-route-circuit
159res/network

express-route-gateway

avm.res.network.express-route-gateway avm.res.network.express-route-gateway
160res/network

express-route-port

avm.res.network.express-route-port avm.res.network.express-route-port
161res/network

firewall-policy

avm.res.network.firewall-policy avm.res.network.firewall-policy
162res/network

front-door

Deprecated
163res/network

front-door-web-application-firewall-policy

avm.res.network.front-door-web-application-firewall-policy avm.res.network.front-door-web-application-firewall-policy
164res/network

ip-group

avm.res.network.ip-group avm.res.network.ip-group
165res/network

load-balancer

avm.res.network.load-balancer avm.res.network.load-balancer
166res/network

local-network-gateway

avm.res.network.local-network-gateway avm.res.network.local-network-gateway
167res/network

nat-gateway

avm.res.network.nat-gateway avm.res.network.nat-gateway
168res/network

network-interface

avm.res.network.network-interface avm.res.network.network-interface
169res/network

network-manager

avm.res.network.network-manager avm.res.network.network-manager
170res/network

network-security-group

avm.res.network.network-security-group avm.res.network.network-security-group
171res/network

network-security-perimeter

avm.res.network.network-security-perimeter avm.res.network.network-security-perimeter
172res/network

network-watcher

avm.res.network.network-watcher avm.res.network.network-watcher
173res/network

p2s-vpn-gateway

avm.res.network.p2s-vpn-gateway avm.res.network.p2s-vpn-gateway
174res/network

private-dns-zone

avm.res.network.private-dns-zone avm.res.network.private-dns-zone
175res/network

private-endpoint

avm.res.network.private-endpoint avm.res.network.private-endpoint
176res/network

private-link-service

avm.res.network.private-link-service avm.res.network.private-link-service
177res/network

public-ip-address

avm.res.network.public-ip-address avm.res.network.public-ip-address
178res/network

public-ip-prefix

avm.res.network.public-ip-prefix avm.res.network.public-ip-prefix
179res/network

route-table

avm.res.network.route-table avm.res.network.route-table
180res/network

service-endpoint-policy

avm.res.network.service-endpoint-policy avm.res.network.service-endpoint-policy
181res/network

trafficmanagerprofile

avm.res.network.trafficmanagerprofile avm.res.network.trafficmanagerprofile
182res/network

virtual-hub

avm.res.network.virtual-hub avm.res.network.virtual-hub
183res/network

virtual-network

avm.res.network.virtual-network avm.res.network.virtual-network
184res/network

virtual-network-gateway

avm.res.network.virtual-network-gateway avm.res.network.virtual-network-gateway
185res/network

virtual-wan

avm.res.network.virtual-wan avm.res.network.virtual-wan
186res/network

vpn-gateway

avm.res.network.vpn-gateway avm.res.network.vpn-gateway
187res/network

vpn-server-configuration

avm.res.network.vpn-server-configuration avm.res.network.vpn-server-configuration
188res/network

vpn-site

avm.res.network.vpn-site avm.res.network.vpn-site
189res/operational-insights

cluster

avm.res.operational-insights.cluster avm.res.operational-insights.cluster
190res/operational-insights

workspace

avm.res.operational-insights.workspace avm.res.operational-insights.workspace
191res/operations-management

solution

avm.res.operations-management.solution avm.res.operations-management.solution
192res/portal

dashboard

avm.res.portal.dashboard avm.res.portal.dashboard
193res/power-bi-dedicated

capacity

avm.res.power-bi-dedicated.capacity avm.res.power-bi-dedicated.capacity
194res/purview

account

avm.res.purview.account avm.res.purview.account
195res/recovery-services

vault

avm.res.recovery-services.vault avm.res.recovery-services.vault
196res/relay

namespace

avm.res.relay.namespace avm.res.relay.namespace
197res/resource-graph

query

avm.res.resource-graph.query avm.res.resource-graph.query
198res/resources

deployment-script

avm.res.resources.deployment-script avm.res.resources.deployment-script
199res/resources

resource-group

avm.res.resources.resource-group avm.res.resources.resource-group
200res/search

search-service

avm.res.search.search-service avm.res.search.search-service
201res/security-insights

data-connector

avm.res.security-insights.data-connector avm.res.security-insights.data-connector
202res/security-insights

setting

avm.res.security-insights.setting avm.res.security-insights.setting
203res/service-bus

namespace

avm.res.service-bus.namespace avm.res.service-bus.namespace
204res/service-fabric

cluster

avm.res.service-fabric.cluster avm.res.service-fabric.cluster
205res/service-networking

traffic-controller

avm.res.service-networking.traffic-controller avm.res.service-networking.traffic-controller
206res/signal-r-service

signal-r

avm.res.signal-r-service.signal-r avm.res.signal-r-service.signal-r
207res/signal-r-service

web-pub-sub

avm.res.signal-r-service.web-pub-sub avm.res.signal-r-service.web-pub-sub
208res/sql

instance-pool

avm.res.sql.instance-pool avm.res.sql.instance-pool
209res/sql

managed-instance

avm.res.sql.managed-instance avm.res.sql.managed-instance
210res/sql

server

avm.res.sql.server avm.res.sql.server
211res/storage

storage-account

avm.res.storage.storage-account avm.res.storage.storage-account
212res/synapse

private-link-hub

avm.res.synapse.private-link-hub avm.res.synapse.private-link-hub
213res/synapse

workspace

avm.res.synapse.workspace avm.res.synapse.workspace
214res/virtual-machine-images

image-template

avm.res.virtual-machine-images.image-template avm.res.virtual-machine-images.image-template
215res/web

connection

avm.res.web.connection avm.res.web.connection
216res/web

hosting-environment

avm.res.web.hosting-environment avm.res.web.hosting-environment
217res/web

serverfarm

avm.res.web.serverfarm avm.res.web.serverfarm
218res/web

site

avm.res.web.site avm.res.web.site
219res/web

static-site

avm.res.web.static-site avm.res.web.static-site
220utl/types

avm-common-types

avm.utl.types.avm-common-types avm.utl.types.avm-common-types

Subsections of Bicep

Bicep Resource Modules

Module catalog

LanguageClassificationPublished 🟢 & 🟡Proposed ⚪SUM
BicepResource17023193
➕ Additional information
Legend

Summary of status icons used on this page

IconStatusDescription
Proposed modulesModules that are proposed and/or being worked on but not published yet.
🟢 & 🟡Published modulesAvailable (🟢) and Orphaned (🟡) modules that are active and usable.
🔴Deprecated modulesModules that reached the end of their lifecycle.
📇All modulesIncluding Published, Proposed and Deprecated ones.

See the Module Lifecycle page for more details.

Info

This page contains various views of the module index (catalog) for Bicep Resource Modules. To see these views, click on the expandable sections with the “➕” sign below.

  • To see the full, unfiltered, unformatted module index on GitHub, click here.

  • To download the source CSV file, click here.

Note

Modules listed below that aren’t shown with the status of Module Available 🟢, are currently in development and are not yet available for use. For proposed modules, see the Proposed modules section below.

Published modules - 🟢 & 🟡

➕ Published Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/aad/domain-serviceAzure Active Directory Domain Service
AAD, Entra ID, Microsoft Entra Domain Services, AAD DS, Azure AD DS
ReneHezser
Rene Hezser
CRYP70N1X
Paul Chirila
02avm/res/alerts-management/action-ruleAction Rulesjudyer28
Justin Dyer
03avm/res/analysis-services/serverAnalysis Services Server
04avm/res/api-management/service
API Management Serviceabhishekaryams
Abhishek Arya
05avm/res/app-configuration/configuration-store
App Configuration StoreJfolberth
John Folberth
06avm/res/app/container-app
Container AppoZakari
Zach Trocinski
07avm/res/app/jobApp JobReneHezser
Rene Hezser
08avm/res/app/managed-environment
  • certificate
  • storage
App Managed Environmentabhishekaryams
Abhishek Arya
09avm/res/app/session-poolApp Session Poolabhishekaryams
Abhishek Arya
10avm/res/authorization/policy-assignment
Authorization - Policy AssignmentAlexanderSehr
Alexander Sehr
11avm/res/authorization/role-assignment
Authorization - Role Assignmentarnoldna
Nate Arnold
12avm/res/automation/automation-account
  • credential
  • hybrid-runbook-worker-group
    • hybrid-runbook-worker
  • job-schedule
  • module
  • powershell72-module
  • python2-package
  • python3-package
  • runbook
  • schedule
  • source-control
  • variable
  • webhook
Automation Accountgpacetti
Giuseppe Pacetti
13avm/res/azure-stack-hci/cluster
  • arc-setting/extension
  • deployment-setting
Azure Stack HCI Clusterchirag1603
Chirag Choudha
14avm/res/azure-stack-hci/logical-networkAzure Stack HCI Logical Networkchirag1603
Chirag Choudha
15avm/res/azure-stack-hci/marketplace-gallery-imageAzure Stack HCI Marketplace Gallery Imagechirag1603
Chirag Choudha
16avm/res/azure-stack-hci/network-interfaceAzure Stack HCI Network Interfacechirag1603
Chirag Choudha
17avm/res/azure-stack-hci/virtual-hard-diskAzure Stack HCI Hard Diskchirag1603
Chirag Choudha
18avm/res/azure-stack-hci/virtual-machine-instanceAzure Stack HCI Virtual Machine Instancechirag1603
Chirag Choudha
19avm/res/batch/batch-accountBatch Accountdidayal-msft
Divyadeep Dayal
20avm/res/cache/redis
  • access-policy
  • access-policy-assignment
  • firewall-rule
  • linked-server
Redis CacheTomazMlakar
Tomaz Mlakar
21avm/res/cache/redis-enterprise
  • database
    • access-policy-assignment
Redis Enterprise CacheJeffreyCA
Jeffrey Chen
22avm/res/cdn/profile
CDN Profile
Azure Front Door
gbeaud
Guillaume Beaud
23avm/res/cognitive-services/account
  • project
Azure AI Services (Cognitive Services)
AI Foundry
mswantek68
Mike Swantek
chanakanissanka
Chanaka Nissanka
24avm/res/communication/communication-serviceCommunication Servicedonk-msft
Don Koning
25avm/res/communication/email-service
Email Communication Servicedonk-msft
Don Koning
26avm/res/compute/availability-setAvailability Set
AS
ahmadabdalla
Ahmad Abdalla
27avm/res/compute/diskCompute Disksegraef
Sebastian Graef
28avm/res/compute/disk-encryption-setDisk Encryption Setsegraef
Sebastian Graef
29avm/res/compute/gallery
  • application
  • image
Azure Compute GalleryReneHezser
Rene Hezser
30avm/res/compute/imageImagetony-box
Tony Box
31avm/res/compute/proximity-placement-groupProximity Placement Groupjeetgarg
Jeet Garg
32avm/res/compute/ssh-public-keyPublic SSH KeyChrisSidebotham
Chris Sidebotham
33avm/res/compute/virtual-machine
Virtual Machine
VM
josunefon
Jordi Sune Fontanals
34avm/res/compute/virtual-machine-scale-set
Virtual Machine Scale Set
VMSS
josunefon
Jordi Sune Fontanals
35avm/res/consumption/budget
Consumption Budgetsegraef
Sebastian Graef
36avm/res/container-instance/container-groupContainer Instance
ACI
JPEasier
Julian Peißker
37avm/res/container-registry/registry
Azure Container Registry (ACR)JPEasier
Julian Peißker
38avm/res/container-service/managed-cluster
  • agent-pool
  • maintenance-configuration
Azure Kubernetes Service (AKS) Managed ClusterJPEasier
Julian Peißker
39avm/res/data-factory/factory
Data FactoryTomazMlakar
Tomaz Mlakar
40avm/res/data-protection/backup-vault
Data Protection Backup Vaultabhishekaryams
Abhishek Arya
41avm/res/databricks/access-connectorAzure Databricks Access Connectorabhishekaryams
Abhishek Arya
42avm/res/databricks/workspaceAzure Databricks Workspaceabhishekaryams
Abhishek Arya
43avm/res/db-for-my-sql/flexible-server
  • administrator
  • advanced-threat-protection
  • configuration
  • database
  • firewall-rule
DB for MySQL Flexible Serverabhishekaryams
Abhishek Arya
44avm/res/db-for-postgre-sql/flexible-server
DB for Postgre SQL Flexible Serverarnoldna
Nate Arnold
45avm/res/desktop-virtualization/application-group
Azure Virtual Desktop (AVD) Application Group
46avm/res/desktop-virtualization/host-poolAzure Virtual Desktop (AVD) Host Pool
47avm/res/desktop-virtualization/scaling-planAzure Virtual Desktop (AVD) Scaling Plan
48avm/res/desktop-virtualization/workspaceAzure Virtual Desktop (AVD) Workspacereikor0x
Vincenzo Sciarra
49avm/res/dev-center/devcenter
Dev Centerahmadabdalla
Ahmad Abdalla
50avm/res/dev-center/network-connectionDev Center Network Connectionahmadabdalla
Ahmad Abdalla
51avm/res/dev-center/project
Dev Center Projectahmadabdalla
Ahmad Abdalla
52avm/res/dev-ops-infrastructure/poolDevOps Infrastructure Poolelizatargithub7
Eliza Tarasila
53avm/res/dev-test-lab/lab
DevTest Labahmadabdalla
Ahmad Abdalla
54avm/res/devices/iot-hubIoT HubTomazMlakar
Tomaz Mlakar
55avm/res/digital-twins/digital-twins-instance
Digital Twins Instanceryanmstephens
Ryan Stephens
56avm/res/document-db/database-account
Cosmos DB Database Accountcmaneu
Christopher Maneu
57avm/res/document-db/mongo-cluster
  • firewall-rule
  • user
Cosmos DB for MongoDB (vCore)sinedied
Yohan Lasorsa
58avm/res/elastic-san/elastic-san
  • volume-group
    • snapshot
    • volume
Elastic SAN
SAN, ESAN, Elastic SAN, Azure Elastic Storage Area Network, iSCSI, internet Small Computer Systems Interface
jbinko
Jiri Binko
59avm/res/event-grid/domain
Event Grid Domainfabmas
Fabio Masciotra
60avm/res/event-grid/namespace
Event Grid Namespacefabmas
Fabio Masciotra
61avm/res/event-grid/system-topic
  • event-subscription
Event Grid System Topicfabmas
Fabio Masciotra
62avm/res/event-grid/topic
Event Grid Topicfabmas
Fabio Masciotra
63avm/res/event-hub/namespace
Event Hub Namespaceabhishekaryams
Abhishek Arya
64avm/res/fabric/capacityFabricmswantek68
Mike Swantek
cmaneu
Christopher Maneu
65avm/res/health-bot/health-botAzure Health BotTomazMlakar
Tomaz Mlakar
66avm/res/healthcare-apis/workspace
Healthcare API Workspace
67avm/res/hybrid-compute/gatewayHybrid Compute Gatewaychirag1603
Chirag Choudha
68avm/res/hybrid-compute/licenseHybrid Compute Licensechirag1603
Chirag Choudha
69avm/res/hybrid-compute/machine
Hybrid Compute Machinechirag1603
Chirag Choudha
70avm/res/hybrid-container-service/provisioned-cluster-instanceHybrid Container Service - Provisioned Cluster Instancechirag1603
Chirag Choudha
71avm/res/insights/action-groupAction Grouprahalan
Rainer Halanek
72avm/res/insights/activity-log-alertActivity Log Alertdonk-msft
Don Koning
73avm/res/insights/component
  • linked-storage-account
Application Insightkrbar
Kris Baranek
74avm/res/insights/data-collection-endpointData Collection Endpointkrbar
Kris Baranek
75avm/res/insights/data-collection-ruleData Collection Rule
DCR
krbar
Kris Baranek
76avm/res/insights/diagnostic-settingDiagnostic Settingkrbar
Kris Baranek
77avm/res/insights/metric-alertMetric Alertkijunkang
Ki Jun Kang
78avm/res/insights/private-link-scope
  • scoped-resource
Azure Monitor Private Link Scopeahmadabdalla
Ahmad Abdalla
79avm/res/insights/scheduled-query-ruleScheduled Query Ruleabhishekaryams
Abhishek Arya
80avm/res/insights/webtestWeb TestJfolberth
John Folberth
81avm/res/key-vault/vault
Key Vault
KV
fblix
Felix Borst
82avm/res/kubernetes-configuration/extensionKubernetes Configuration ExtensionJPEasier
Julian Peißker
83avm/res/kubernetes-configuration/flux-configurationKubernetes Configuration Flux ConfigurationJPEasier
Julian Peißker
84avm/res/kubernetes-runtime/load-balancerKubernetes Runtime Load Balancerchirag1603
Chirag Choudha
85avm/res/kubernetes/connected-clusterKubernetes Connected Clusterchirag1603
Chirag Choudha
86avm/res/kusto/cluster
  • database
    • principal-assignment
  • principal-assignment
Azure Data Explorer (Kusto) clusteroZakari
Zach Trocinski
87avm/res/load-test-service/load-testLoad Testing Servicesebassem
Seif Bassem
88avm/res/logic/integration-account
Logic Apps Integration Accountlsnoddy
Luke Snoddy
89avm/res/logic/workflowLogic Apps Workflowlsnoddy
Luke Snoddy
90avm/res/machine-learning-services/registryMachine Learning Services Registryjosunefon
Jordi Sune Fontanals
91avm/res/machine-learning-services/workspace
  • compute
  • connection
  • datastore
Machine Learning Services Workspace
ML Workspace
cecheta
Chinedum Echeta
ross-p-smith
Ross Smith
92avm/res/maintenance/configuration-assignmentMaintenance Configuration Assignmenteriqua
Erika Gressi
93avm/res/maintenance/maintenance-configurationMaintenance Configurationarievanderwende
Arie van der Wende
94avm/res/managed-identity/user-assigned-identity
User Assigned Identity
MSI
gpacetti
Giuseppe Pacetti
95avm/res/managed-services/registration-definitionRegistration Definition (Lighthouse)
96avm/res/management/management-groupManagement Group
MG
fblix
Felix Borst
97avm/res/management/service-groupService Groupjtracey93
Jack Tracey
98avm/res/maps/accountAzure Maps Accountjhueppauff
Julian Huppauff
99avm/res/net-app/net-app-account
Azure NetApp Filefbinotto
Felipe Binotto
100avm/res/network/application-gatewayApplication Gateway
App GW
toddbeauchemin
Todd Beauchemin
101avm/res/network/application-gateway-web-application-firewall-policyApplication Gateway Web Application Firewall (WAF) Policytoddbeauchemin
Todd Beauchemin
102avm/res/network/application-security-groupApplication Security Group (ASG)
ASG
segraef
Sebastian Graef
103avm/res/network/azure-firewallAzure Firewall
Azure FW
jtracey93
Jack Tracey
oZakari
Zach Trocinski
104avm/res/network/bastion-hostBastion Hostkrbar
Kris Baranek
105avm/res/network/connectionVirtual Network Gateway Connectionfabmas
Fabio Masciotra
106avm/res/network/ddos-protection-planDDoS Protection Plansegraef
Sebastian Graef
107avm/res/network/dns-forwarding-ruleset
  • forwarding-rule
  • virtual-network-link
DNS Forwarding RulesetChrisSidebotham
Chris Sidebotham
108avm/res/network/dns-resolver
  • inbound-endpoint
  • outbound-endpoint
DNS ResolverChrisSidebotham
Chris Sidebotham
109avm/res/network/dns-zone
  • a
  • aaaa
  • caa
  • cname
  • dnssec-config
  • mx
  • ns
  • ptr
  • soa
  • srv
  • txt
Public DNS ZoneChrisSidebotham
Chris Sidebotham
110avm/res/network/express-route-circuitExpressRoute Circuit
ER Circuit
arnoldna
Nate Arnold
111avm/res/network/express-route-gatewayExpress Route Gateway
ER GW
arnoldna
Nate Arnold
112avm/res/network/express-route-portExpressRoute Port
ER Port
arnoldna
Nate Arnold
113avm/res/network/firewall-policy
  • rule-collection-group
Firewall Policyjtracey93
Jack Tracey
oZakari
Zach Trocinski
114avm/res/network/front-door-web-application-firewall-policyFront Door Web Application Firewall (WAF) PolicyPaulJohnston88
Paul Johnston
115avm/res/network/ip-groupIP Groupahmadabdalla
Ahmad Abdalla
116avm/res/network/load-balancer
  • backend-address-pool
  • inbound-nat-rule
Load Balancer
LB, NLB
arnoldna
Nate Arnold
117avm/res/network/local-network-gatewayLocal Network Gatewayfabmas
Fabio Masciotra
118avm/res/network/nat-gatewayNAT Gateway
NAT GW
fabmas
Fabio Masciotra
119avm/res/network/network-interfaceNetwork Interface
NIC
rahalan
Rainer Halanek
120avm/res/network/network-manager
  • connectivity-configuration
  • network-group
    • static-member
  • routing-configuration
    • rule-collection
      • rule
  • scope-connection
  • security-admin-configuration
    • rule-collection
      • rule
Network Managerahmadabdalla
Ahmad Abdalla
121avm/res/network/network-security-groupNetwork Security Group
NSG
ahmadabdalla
Ahmad Abdalla
122avm/res/network/network-security-perimeter
  • profile
    • access-rule
Network Security Perimeterpeterbud
Peter Budai
123avm/res/network/network-watcher
  • connection-monitor
  • flow-log
Network Watchersegraef
Sebastian Graef
124avm/res/network/p2s-vpn-gatewayP2S VPN Gatewayericscheffler
Eric Scheffler
125avm/res/network/private-dns-zone
Private DNS ZoneChrisSidebotham
Chris Sidebotham
126avm/res/network/private-endpoint
  • private-dns-zone-group
Private Endpointsegraef
Sebastian Graef
127avm/res/network/private-link-servicePrivate Link Serviceahmadabdalla
Ahmad Abdalla
128avm/res/network/public-ip-addressPublic IP Address
PIP
ChrisSidebotham
Chris Sidebotham
krbar
Kris Baranek
129avm/res/network/public-ip-prefixPublic IP Prefix
PIP Prefix
krbar
Kris Baranek
130avm/res/network/route-tableRoute Table
UDR
segraef
Sebastian Graef
131avm/res/network/service-endpoint-policyService Endpoint Policyjeetgarg
Jeet Garg
132avm/res/network/trafficmanagerprofileTraffic Manager Profilelsnoddy
Luke Snoddy
133avm/res/network/virtual-hub
  • hub-route-table
  • hub-virtual-network-connection
  • route-map
  • routing-intent
Virtual Hubarnoldna
Nate Arnold
134avm/res/network/virtual-network
Virtual Network
VNET
mjrich19
MJ Richardson
135avm/res/network/virtual-network-gateway
  • nat-rule
Virtual Network Gateway
VNET GW
fabmas
Fabio Masciotra
136avm/res/network/virtual-wanVirtual WAN
vWAN
arnoldna
Nate Arnold
137avm/res/network/vpn-gateway
  • nat-rule
  • vpn-connection
VPN Gateway
VPN GW
fabmas
Fabio Masciotra
138avm/res/network/vpn-server-configurationVPN Server Configurationericscheffler
Eric Scheffler
139avm/res/network/vpn-siteVPN Sitefabmas
Fabio Masciotra
140avm/res/operational-insights/clusterLog Analytics Dedicated Clusterabhishekaryams
Abhishek Arya
141avm/res/operational-insights/workspace
Log Analytics Workspacekrbar
Kris Baranek
142avm/res/operations-management/solutionOperations Management Solutionkrbar
Kris Baranek
143avm/res/portal/dashboardPortal Dashboardkrbar
Kris Baranek
144avm/res/power-bi-dedicated/capacityPower BI Dedicated CapacityChrisSidebotham
Chris Sidebotham
145avm/res/purview/accountPurview Accountabhishekaryams
Abhishek Arya
146avm/res/recovery-services/vault
  • backup-config
  • backup-fabric/protection-container/protected-item
  • backup-policy
  • replication-alert-setting
  • replication-fabric
    • replication-protection-container
      • replication-protection-container-mapping
  • replication-policy
Recovery Services Vaultalexanderojala
Alexander Ojala
147avm/res/relay/namespace
Relay Namespace
148avm/res/resource-graph/queryResource Graph Querysebassem
Seif Bassem
149avm/res/resources/deployment-scriptDeployment Scriptsebassem
Seif Bassem
150avm/res/resources/resource-groupResource Group
RG
segraef
Sebastian Graef
151avm/res/search/search-service
  • shared-private-link-resource
Search Servicekrbar
Kris Baranek
152avm/res/security-insights/data-connectorSecurity Insights - Data Connector
153avm/res/security-insights/settingSecurity Insights - SettingTomazMlakar
Tomaz Mlakar
154avm/res/service-bus/namespace
  • authorization-rule
  • disaster-recovery-config
  • migration-configuration
  • network-rule-set
  • queue
    • authorization-rule
  • topic
    • authorization-rule
    • subscription
      • rule
Service Bus NamespaceChrisSidebotham
Chris Sidebotham
155avm/res/service-fabric/cluster
  • application-type
Service Fabric Clusterlsnoddy
Luke Snoddy
156avm/res/service-networking/traffic-controller
Application Gateway for Containers (Traffic Controller)krbar
Kris Baranek
157avm/res/signal-r-service/signal-rSignalR Service SignalR
158avm/res/signal-r-service/web-pub-subSignalR Web PubSub Service
159avm/res/sql/instance-poolSQL Instance Poolgpacetti
Giuseppe Pacetti
160avm/res/sql/managed-instance
  • database
    • backup-long-term-retention-policy
    • backup-short-term-retention-policy
  • encryption-protector
  • key
  • security-alert-policy
  • vulnerability-assessment
SQL Managed Instance
SQL MI
chanakanissanka
Chanaka Nissanka
161avm/res/sql/server
  • auditing-setting
  • database
    • backup-long-term-retention-policy
    • backup-short-term-retention-policy
  • elastic-pool
  • encryption-protector
  • failover-group
  • firewall-rule
  • key
  • security-alert-policy
  • virtual-network-rule
  • vulnerability-assessment
Azure SQL Serverpeterbud
Peter Budai
162avm/res/storage/storage-account
Storage Accountfblix
Felix Borst
163avm/res/synapse/private-link-hubAzure Synapse Analytics Private Link HubTomazMlakar
Tomaz Mlakar
164avm/res/synapse/workspace
  • administrator
  • big-data-pool
  • firewall-rule
  • integration-runtime
  • key
  • sql-pool
Azure Synapse Analytics WorkspaceTomazMlakar
Tomaz Mlakar
165avm/res/virtual-machine-images/image-templateVirtual Machine Image Templateahmadabdalla
Ahmad Abdalla
166avm/res/web/connectionAPI Connectionabhishekaryams
Abhishek Arya
167avm/res/web/hosting-environment
  • configuration
App Service Environment
ASE
tsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal
168avm/res/web/serverfarmApp Service Plantsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal
169avm/res/web/site
  • basic-publishing-credentials-policy
  • config
  • extension
  • hybrid-connection-namespace/relay
  • slot
    • basic-publishing-credentials-policy
    • config
    • extension
    • hybrid-connection-namespace/relay
Web/Function App
App Service, Web Site, Logic App, Function App
tsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal
170avm/res/web/static-site
  • config
  • custom-domain
  • linked-backend
Static Web AppChrisSidebotham
Chris Sidebotham

Proposed modules - ⚪

➕ Proposed Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/api-center/serviceAPI Center Servicemaciejporebski
Maciej Porebski
02avm/res/app/agentAzure SRE AgentFallenHoot
Zach Olinske
03avm/res/avs/private-cloudAVS Private Cloudjchancellor-ms
Jon Chancellor
04avm/res/bot-service/bot-serviceBot ServiceAnkitSDesai
Ankit Desai
asishr
Asish R
05avm/res/chaos/experimentChaos Experiment
Azure Chaos Studio, Chaos Engineering
jbinko
Jiri Binko
06avm/res/code-signing/code-signing-accountAzure Artifact Signing
07avm/res/dashboard/grafanaAzure Managed Grafanavlahane
Vishal Lahane
08avm/res/data-protection/resource-guardData Protection Resource Guard
09avm/res/durable-task/schedulerDurable Task Schedulergreenie-msft
Nick Greenfield
10avm/res/edge-order/order-itemEdge Order Itemchirag1603
Chirag Choudha
11avm/res/edge/configurationEdge Configurationchirag1603
Chirag Choudha
12avm/res/edge/site
  • rg-scope
  • sub-scope
Edge Sitechirag1603
Chirag Choudha
13avm/res/hybrid-compute/private-link-scopeHybrid Compute Private Link Scopechirag1603
Chirag Choudha
14avm/res/hybrid-compute/settingHybrid Compute Settingchirag1603
Chirag Choudha
15avm/res/insights/autoscale-settingInsights - Auto Scale SettingFallenHoot
Zach Olinske
16avm/res/iot-operations/instanceIoT Operations Instanceagreaves-ms
Allen Greaves
17avm/res/key-vault/managed-hsmManaged HSMns-github-design
Nasreen Sarah
18avm/res/kubernetes-runtime/bpg-peerKubernetes Runtime BGP Peerchirag1603
Chirag Choudha
19avm/res/kubernetes-runtime/serviceKubernetes Runtime Servicechirag1603
Chirag Choudha
20avm/res/scom/managed-instanceSCOM MI
System Center Operations Manager - Managed Instance

21avm/res/security-insights/onboarding-stateSecurity Insights - Onboarding State
22avm/res/sql-virtual-machine/sql-virtual-machineSQL Virtual Machine
SQL VM
peterbud
Peter Budai
23avm/res/stream-analytics/streaming-jobStream Analytics Job

Deprecated modules - 🔴

➕ Deprecated Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/network/front-doorAzure Front Door

All modules - 📇

➕ All Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/aad/domain-serviceAzure Active Directory Domain Service
AAD, Entra ID, Microsoft Entra Domain Services, AAD DS, Azure AD DS
ReneHezser
Rene Hezser
CRYP70N1X
Paul Chirila
02avm/res/alerts-management/action-ruleAction Rulesjudyer28
Justin Dyer
03avm/res/analysis-services/serverAnalysis Services Server
04avm/res/api-center/serviceAPI Center Servicemaciejporebski
Maciej Porebski
05avm/res/api-management/service
API Management Serviceabhishekaryams
Abhishek Arya
06avm/res/app-configuration/configuration-store
App Configuration StoreJfolberth
John Folberth
07avm/res/app/agentAzure SRE AgentFallenHoot
Zach Olinske
08avm/res/app/container-app
Container AppoZakari
Zach Trocinski
09avm/res/app/jobApp JobReneHezser
Rene Hezser
10avm/res/app/managed-environment
  • certificate
  • storage
App Managed Environmentabhishekaryams
Abhishek Arya
11avm/res/app/session-poolApp Session Poolabhishekaryams
Abhishek Arya
12avm/res/authorization/policy-assignment
Authorization - Policy AssignmentAlexanderSehr
Alexander Sehr
13avm/res/authorization/role-assignment
Authorization - Role Assignmentarnoldna
Nate Arnold
14avm/res/automation/automation-account
  • credential
  • hybrid-runbook-worker-group
    • hybrid-runbook-worker
  • job-schedule
  • module
  • powershell72-module
  • python2-package
  • python3-package
  • runbook
  • schedule
  • source-control
  • variable
  • webhook
Automation Accountgpacetti
Giuseppe Pacetti
15avm/res/avs/private-cloudAVS Private Cloudjchancellor-ms
Jon Chancellor
16avm/res/azure-stack-hci/cluster
  • arc-setting/extension
  • deployment-setting
Azure Stack HCI Clusterchirag1603
Chirag Choudha
17avm/res/azure-stack-hci/logical-networkAzure Stack HCI Logical Networkchirag1603
Chirag Choudha
18avm/res/azure-stack-hci/marketplace-gallery-imageAzure Stack HCI Marketplace Gallery Imagechirag1603
Chirag Choudha
19avm/res/azure-stack-hci/network-interfaceAzure Stack HCI Network Interfacechirag1603
Chirag Choudha
20avm/res/azure-stack-hci/virtual-hard-diskAzure Stack HCI Hard Diskchirag1603
Chirag Choudha
21avm/res/azure-stack-hci/virtual-machine-instanceAzure Stack HCI Virtual Machine Instancechirag1603
Chirag Choudha
22avm/res/batch/batch-accountBatch Accountdidayal-msft
Divyadeep Dayal
23avm/res/bot-service/bot-serviceBot ServiceAnkitSDesai
Ankit Desai
asishr
Asish R
24avm/res/cache/redis
  • access-policy
  • access-policy-assignment
  • firewall-rule
  • linked-server
Redis CacheTomazMlakar
Tomaz Mlakar
25avm/res/cache/redis-enterprise
  • database
    • access-policy-assignment
Redis Enterprise CacheJeffreyCA
Jeffrey Chen
26avm/res/cdn/profile
CDN Profile
Azure Front Door
gbeaud
Guillaume Beaud
27avm/res/chaos/experimentChaos Experiment
Azure Chaos Studio, Chaos Engineering
jbinko
Jiri Binko
28avm/res/code-signing/code-signing-accountAzure Artifact Signing
29avm/res/cognitive-services/account
  • project
Azure AI Services (Cognitive Services)
AI Foundry
mswantek68
Mike Swantek
chanakanissanka
Chanaka Nissanka
30avm/res/communication/communication-serviceCommunication Servicedonk-msft
Don Koning
31avm/res/communication/email-service
Email Communication Servicedonk-msft
Don Koning
32avm/res/compute/availability-setAvailability Set
AS
ahmadabdalla
Ahmad Abdalla
33avm/res/compute/diskCompute Disksegraef
Sebastian Graef
34avm/res/compute/disk-encryption-setDisk Encryption Setsegraef
Sebastian Graef
35avm/res/compute/gallery
  • application
  • image
Azure Compute GalleryReneHezser
Rene Hezser
36avm/res/compute/imageImagetony-box
Tony Box
37avm/res/compute/proximity-placement-groupProximity Placement Groupjeetgarg
Jeet Garg
38avm/res/compute/ssh-public-keyPublic SSH KeyChrisSidebotham
Chris Sidebotham
39avm/res/compute/virtual-machine
Virtual Machine
VM
josunefon
Jordi Sune Fontanals
40avm/res/compute/virtual-machine-scale-set
Virtual Machine Scale Set
VMSS
josunefon
Jordi Sune Fontanals
41avm/res/consumption/budget
Consumption Budgetsegraef
Sebastian Graef
42avm/res/container-instance/container-groupContainer Instance
ACI
JPEasier
Julian Peißker
43avm/res/container-registry/registry
Azure Container Registry (ACR)JPEasier
Julian Peißker
44avm/res/container-service/managed-cluster
  • agent-pool
  • maintenance-configuration
Azure Kubernetes Service (AKS) Managed ClusterJPEasier
Julian Peißker
45avm/res/dashboard/grafanaAzure Managed Grafanavlahane
Vishal Lahane
46avm/res/data-factory/factory
Data FactoryTomazMlakar
Tomaz Mlakar
47avm/res/data-protection/backup-vault
Data Protection Backup Vaultabhishekaryams
Abhishek Arya
48avm/res/data-protection/resource-guardData Protection Resource Guard
49avm/res/databricks/access-connectorAzure Databricks Access Connectorabhishekaryams
Abhishek Arya
50avm/res/databricks/workspaceAzure Databricks Workspaceabhishekaryams
Abhishek Arya
51avm/res/db-for-my-sql/flexible-server
  • administrator
  • advanced-threat-protection
  • configuration
  • database
  • firewall-rule
DB for MySQL Flexible Serverabhishekaryams
Abhishek Arya
52avm/res/db-for-postgre-sql/flexible-server
DB for Postgre SQL Flexible Serverarnoldna
Nate Arnold
53avm/res/desktop-virtualization/application-group
Azure Virtual Desktop (AVD) Application Group
54avm/res/desktop-virtualization/host-poolAzure Virtual Desktop (AVD) Host Pool
55avm/res/desktop-virtualization/scaling-planAzure Virtual Desktop (AVD) Scaling Plan
56avm/res/desktop-virtualization/workspaceAzure Virtual Desktop (AVD) Workspacereikor0x
Vincenzo Sciarra
57avm/res/dev-center/devcenter
Dev Centerahmadabdalla
Ahmad Abdalla
58avm/res/dev-center/network-connectionDev Center Network Connectionahmadabdalla
Ahmad Abdalla
59avm/res/dev-center/project
Dev Center Projectahmadabdalla
Ahmad Abdalla
60avm/res/dev-ops-infrastructure/poolDevOps Infrastructure Poolelizatargithub7
Eliza Tarasila
61avm/res/dev-test-lab/lab
DevTest Labahmadabdalla
Ahmad Abdalla
62avm/res/devices/iot-hubIoT HubTomazMlakar
Tomaz Mlakar
63avm/res/digital-twins/digital-twins-instance
Digital Twins Instanceryanmstephens
Ryan Stephens
64avm/res/document-db/database-account
Cosmos DB Database Accountcmaneu
Christopher Maneu
65avm/res/document-db/mongo-cluster
  • firewall-rule
  • user
Cosmos DB for MongoDB (vCore)sinedied
Yohan Lasorsa
66avm/res/durable-task/schedulerDurable Task Schedulergreenie-msft
Nick Greenfield
67avm/res/edge-order/order-itemEdge Order Itemchirag1603
Chirag Choudha
68avm/res/edge/configurationEdge Configurationchirag1603
Chirag Choudha
69avm/res/edge/site
  • rg-scope
  • sub-scope
Edge Sitechirag1603
Chirag Choudha
70avm/res/elastic-san/elastic-san
  • volume-group
    • snapshot
    • volume
Elastic SAN
SAN, ESAN, Elastic SAN, Azure Elastic Storage Area Network, iSCSI, internet Small Computer Systems Interface
jbinko
Jiri Binko
71avm/res/event-grid/domain
Event Grid Domainfabmas
Fabio Masciotra
72avm/res/event-grid/namespace
Event Grid Namespacefabmas
Fabio Masciotra
73avm/res/event-grid/system-topic
  • event-subscription
Event Grid System Topicfabmas
Fabio Masciotra
74avm/res/event-grid/topic
Event Grid Topicfabmas
Fabio Masciotra
75avm/res/event-hub/namespace
Event Hub Namespaceabhishekaryams
Abhishek Arya
76avm/res/fabric/capacityFabricmswantek68
Mike Swantek
cmaneu
Christopher Maneu
77avm/res/health-bot/health-botAzure Health BotTomazMlakar
Tomaz Mlakar
78avm/res/healthcare-apis/workspace
Healthcare API Workspace
79avm/res/hybrid-compute/gatewayHybrid Compute Gatewaychirag1603
Chirag Choudha
80avm/res/hybrid-compute/licenseHybrid Compute Licensechirag1603
Chirag Choudha
81avm/res/hybrid-compute/machine
Hybrid Compute Machinechirag1603
Chirag Choudha
82avm/res/hybrid-compute/private-link-scopeHybrid Compute Private Link Scopechirag1603
Chirag Choudha
83avm/res/hybrid-compute/settingHybrid Compute Settingchirag1603
Chirag Choudha
84avm/res/hybrid-container-service/provisioned-cluster-instanceHybrid Container Service - Provisioned Cluster Instancechirag1603
Chirag Choudha
85avm/res/insights/action-groupAction Grouprahalan
Rainer Halanek
86avm/res/insights/activity-log-alertActivity Log Alertdonk-msft
Don Koning
87avm/res/insights/autoscale-settingInsights - Auto Scale SettingFallenHoot
Zach Olinske
88avm/res/insights/component
  • linked-storage-account
Application Insightkrbar
Kris Baranek
89avm/res/insights/data-collection-endpointData Collection Endpointkrbar
Kris Baranek
90avm/res/insights/data-collection-ruleData Collection Rule
DCR
krbar
Kris Baranek
91avm/res/insights/diagnostic-settingDiagnostic Settingkrbar
Kris Baranek
92avm/res/insights/metric-alertMetric Alertkijunkang
Ki Jun Kang
93avm/res/insights/private-link-scope
  • scoped-resource
Azure Monitor Private Link Scopeahmadabdalla
Ahmad Abdalla
94avm/res/insights/scheduled-query-ruleScheduled Query Ruleabhishekaryams
Abhishek Arya
95avm/res/insights/webtestWeb TestJfolberth
John Folberth
96avm/res/iot-operations/instanceIoT Operations Instanceagreaves-ms
Allen Greaves
97avm/res/key-vault/managed-hsmManaged HSMns-github-design
Nasreen Sarah
98avm/res/key-vault/vault
Key Vault
KV
fblix
Felix Borst
99avm/res/kubernetes-configuration/extensionKubernetes Configuration ExtensionJPEasier
Julian Peißker
100avm/res/kubernetes-configuration/flux-configurationKubernetes Configuration Flux ConfigurationJPEasier
Julian Peißker
101avm/res/kubernetes-runtime/bpg-peerKubernetes Runtime BGP Peerchirag1603
Chirag Choudha
102avm/res/kubernetes-runtime/load-balancerKubernetes Runtime Load Balancerchirag1603
Chirag Choudha
103avm/res/kubernetes-runtime/serviceKubernetes Runtime Servicechirag1603
Chirag Choudha
104avm/res/kubernetes/connected-clusterKubernetes Connected Clusterchirag1603
Chirag Choudha
105avm/res/kusto/cluster
  • database
    • principal-assignment
  • principal-assignment
Azure Data Explorer (Kusto) clusteroZakari
Zach Trocinski
106avm/res/load-test-service/load-testLoad Testing Servicesebassem
Seif Bassem
107avm/res/logic/integration-account
Logic Apps Integration Accountlsnoddy
Luke Snoddy
108avm/res/logic/workflowLogic Apps Workflowlsnoddy
Luke Snoddy
109avm/res/machine-learning-services/registryMachine Learning Services Registryjosunefon
Jordi Sune Fontanals
110avm/res/machine-learning-services/workspace
  • compute
  • connection
  • datastore
Machine Learning Services Workspace
ML Workspace
cecheta
Chinedum Echeta
ross-p-smith
Ross Smith
111avm/res/maintenance/configuration-assignmentMaintenance Configuration Assignmenteriqua
Erika Gressi
112avm/res/maintenance/maintenance-configurationMaintenance Configurationarievanderwende
Arie van der Wende
113avm/res/managed-identity/user-assigned-identity
User Assigned Identity
MSI
gpacetti
Giuseppe Pacetti
114avm/res/managed-services/registration-definitionRegistration Definition (Lighthouse)
115avm/res/management/management-groupManagement Group
MG
fblix
Felix Borst
116avm/res/management/service-groupService Groupjtracey93
Jack Tracey
117avm/res/maps/accountAzure Maps Accountjhueppauff
Julian Huppauff
118avm/res/net-app/net-app-account
Azure NetApp Filefbinotto
Felipe Binotto
119avm/res/network/application-gatewayApplication Gateway
App GW
toddbeauchemin
Todd Beauchemin
120avm/res/network/application-gateway-web-application-firewall-policyApplication Gateway Web Application Firewall (WAF) Policytoddbeauchemin
Todd Beauchemin
121avm/res/network/application-security-groupApplication Security Group (ASG)
ASG
segraef
Sebastian Graef
122avm/res/network/azure-firewallAzure Firewall
Azure FW
jtracey93
Jack Tracey
oZakari
Zach Trocinski
123avm/res/network/bastion-hostBastion Hostkrbar
Kris Baranek
124avm/res/network/connectionVirtual Network Gateway Connectionfabmas
Fabio Masciotra
125avm/res/network/ddos-protection-planDDoS Protection Plansegraef
Sebastian Graef
126avm/res/network/dns-forwarding-ruleset
  • forwarding-rule
  • virtual-network-link
DNS Forwarding RulesetChrisSidebotham
Chris Sidebotham
127avm/res/network/dns-resolver
  • inbound-endpoint
  • outbound-endpoint
DNS ResolverChrisSidebotham
Chris Sidebotham
128avm/res/network/dns-zone
  • a
  • aaaa
  • caa
  • cname
  • dnssec-config
  • mx
  • ns
  • ptr
  • soa
  • srv
  • txt
Public DNS ZoneChrisSidebotham
Chris Sidebotham
129avm/res/network/express-route-circuitExpressRoute Circuit
ER Circuit
arnoldna
Nate Arnold
130avm/res/network/express-route-gatewayExpress Route Gateway
ER GW
arnoldna
Nate Arnold
131avm/res/network/express-route-portExpressRoute Port
ER Port
arnoldna
Nate Arnold
132avm/res/network/firewall-policy
  • rule-collection-group
Firewall Policyjtracey93
Jack Tracey
oZakari
Zach Trocinski
133avm/res/network/front-doorAzure Front Door
134avm/res/network/front-door-web-application-firewall-policyFront Door Web Application Firewall (WAF) PolicyPaulJohnston88
Paul Johnston
135avm/res/network/ip-groupIP Groupahmadabdalla
Ahmad Abdalla
136avm/res/network/load-balancer
  • backend-address-pool
  • inbound-nat-rule
Load Balancer
LB, NLB
arnoldna
Nate Arnold
137avm/res/network/local-network-gatewayLocal Network Gatewayfabmas
Fabio Masciotra
138avm/res/network/nat-gatewayNAT Gateway
NAT GW
fabmas
Fabio Masciotra
139avm/res/network/network-interfaceNetwork Interface
NIC
rahalan
Rainer Halanek
140avm/res/network/network-manager
  • connectivity-configuration
  • network-group
    • static-member
  • routing-configuration
    • rule-collection
      • rule
  • scope-connection
  • security-admin-configuration
    • rule-collection
      • rule
Network Managerahmadabdalla
Ahmad Abdalla
141avm/res/network/network-security-groupNetwork Security Group
NSG
ahmadabdalla
Ahmad Abdalla
142avm/res/network/network-security-perimeter
  • profile
    • access-rule
Network Security Perimeterpeterbud
Peter Budai
143avm/res/network/network-watcher
  • connection-monitor
  • flow-log
Network Watchersegraef
Sebastian Graef
144avm/res/network/p2s-vpn-gatewayP2S VPN Gatewayericscheffler
Eric Scheffler
145avm/res/network/private-dns-zone
Private DNS ZoneChrisSidebotham
Chris Sidebotham
146avm/res/network/private-endpoint
  • private-dns-zone-group
Private Endpointsegraef
Sebastian Graef
147avm/res/network/private-link-servicePrivate Link Serviceahmadabdalla
Ahmad Abdalla
148avm/res/network/public-ip-addressPublic IP Address
PIP
ChrisSidebotham
Chris Sidebotham
krbar
Kris Baranek
149avm/res/network/public-ip-prefixPublic IP Prefix
PIP Prefix
krbar
Kris Baranek
150avm/res/network/route-tableRoute Table
UDR
segraef
Sebastian Graef
151avm/res/network/service-endpoint-policyService Endpoint Policyjeetgarg
Jeet Garg
152avm/res/network/trafficmanagerprofileTraffic Manager Profilelsnoddy
Luke Snoddy
153avm/res/network/virtual-hub
  • hub-route-table
  • hub-virtual-network-connection
  • route-map
  • routing-intent
Virtual Hubarnoldna
Nate Arnold
154avm/res/network/virtual-network
Virtual Network
VNET
mjrich19
MJ Richardson
155avm/res/network/virtual-network-gateway
  • nat-rule
Virtual Network Gateway
VNET GW
fabmas
Fabio Masciotra
156avm/res/network/virtual-wanVirtual WAN
vWAN
arnoldna
Nate Arnold
157avm/res/network/vpn-gateway
  • nat-rule
  • vpn-connection
VPN Gateway
VPN GW
fabmas
Fabio Masciotra
158avm/res/network/vpn-server-configurationVPN Server Configurationericscheffler
Eric Scheffler
159avm/res/network/vpn-siteVPN Sitefabmas
Fabio Masciotra
160avm/res/operational-insights/clusterLog Analytics Dedicated Clusterabhishekaryams
Abhishek Arya
161avm/res/operational-insights/workspace
Log Analytics Workspacekrbar
Kris Baranek
162avm/res/operations-management/solutionOperations Management Solutionkrbar
Kris Baranek
163avm/res/portal/dashboardPortal Dashboardkrbar
Kris Baranek
164avm/res/power-bi-dedicated/capacityPower BI Dedicated CapacityChrisSidebotham
Chris Sidebotham
165avm/res/purview/accountPurview Accountabhishekaryams
Abhishek Arya
166avm/res/recovery-services/vault
  • backup-config
  • backup-fabric/protection-container/protected-item
  • backup-policy
  • replication-alert-setting
  • replication-fabric
    • replication-protection-container
      • replication-protection-container-mapping
  • replication-policy
Recovery Services Vaultalexanderojala
Alexander Ojala
167avm/res/relay/namespace
Relay Namespace
168avm/res/resource-graph/queryResource Graph Querysebassem
Seif Bassem
169avm/res/resources/deployment-scriptDeployment Scriptsebassem
Seif Bassem
170avm/res/resources/resource-groupResource Group
RG
segraef
Sebastian Graef
171avm/res/scom/managed-instanceSCOM MI
System Center Operations Manager - Managed Instance

172avm/res/search/search-service
  • shared-private-link-resource
Search Servicekrbar
Kris Baranek
173avm/res/security-insights/data-connectorSecurity Insights - Data Connector
174avm/res/security-insights/onboarding-stateSecurity Insights - Onboarding State
175avm/res/security-insights/settingSecurity Insights - SettingTomazMlakar
Tomaz Mlakar
176avm/res/service-bus/namespace
  • authorization-rule
  • disaster-recovery-config
  • migration-configuration
  • network-rule-set
  • queue
    • authorization-rule
  • topic
    • authorization-rule
    • subscription
      • rule
Service Bus NamespaceChrisSidebotham
Chris Sidebotham
177avm/res/service-fabric/cluster
  • application-type
Service Fabric Clusterlsnoddy
Luke Snoddy
178avm/res/service-networking/traffic-controller
Application Gateway for Containers (Traffic Controller)krbar
Kris Baranek
179avm/res/signal-r-service/signal-rSignalR Service SignalR
180avm/res/signal-r-service/web-pub-subSignalR Web PubSub Service
181avm/res/sql-virtual-machine/sql-virtual-machineSQL Virtual Machine
SQL VM
peterbud
Peter Budai
182avm/res/sql/instance-poolSQL Instance Poolgpacetti
Giuseppe Pacetti
183avm/res/sql/managed-instance
  • database
    • backup-long-term-retention-policy
    • backup-short-term-retention-policy
  • encryption-protector
  • key
  • security-alert-policy
  • vulnerability-assessment
SQL Managed Instance
SQL MI
chanakanissanka
Chanaka Nissanka
184avm/res/sql/server
  • auditing-setting
  • database
    • backup-long-term-retention-policy
    • backup-short-term-retention-policy
  • elastic-pool
  • encryption-protector
  • failover-group
  • firewall-rule
  • key
  • security-alert-policy
  • virtual-network-rule
  • vulnerability-assessment
Azure SQL Serverpeterbud
Peter Budai
185avm/res/storage/storage-account
Storage Accountfblix
Felix Borst
186avm/res/stream-analytics/streaming-jobStream Analytics Job
187avm/res/synapse/private-link-hubAzure Synapse Analytics Private Link HubTomazMlakar
Tomaz Mlakar
188avm/res/synapse/workspace
  • administrator
  • big-data-pool
  • firewall-rule
  • integration-runtime
  • key
  • sql-pool
Azure Synapse Analytics WorkspaceTomazMlakar
Tomaz Mlakar
189avm/res/virtual-machine-images/image-templateVirtual Machine Image Templateahmadabdalla
Ahmad Abdalla
190avm/res/web/connectionAPI Connectionabhishekaryams
Abhishek Arya
191avm/res/web/hosting-environment
  • configuration
App Service Environment
ASE
tsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal
192avm/res/web/serverfarmApp Service Plantsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal
193avm/res/web/site
  • basic-publishing-credentials-policy
  • config
  • extension
  • hybrid-connection-namespace/relay
  • slot
    • basic-publishing-credentials-policy
    • config
    • extension
    • hybrid-connection-namespace/relay
Web/Function App
App Service, Web Site, Logic App, Function App
tsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal
194avm/res/web/static-site
  • config
  • custom-domain
  • linked-backend
Static Web AppChrisSidebotham
Chris Sidebotham

Module Publication History - 📅

➕ Module Publication History - Module names, status and owners

Modules published in May 2026

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/communication/email-service/domainEmail Communication Service - Domain (Child of avm/res/communication/email-service)
(Inherited): donk-msft
Don Koning
02avm/res/communication/email-service/domain/sender-usernameEmail Communication Service - Domain - Sender Username (Child of avm/res/communication/email-service)
(Inherited): donk-msft
Don Koning
03avm/res/compute/virtual-machine-scale-set/extensionVirtual Machine Scale Set - Extension (Child of avm/res/compute/virtual-machine-scale-set)
(Inherited): josunefon
Jordi Sune Fontanals
04avm/res/compute/virtual-machine/extensionVirtual Machine - Extension (Child of avm/res/compute/virtual-machine)
(Inherited): josunefon
Jordi Sune Fontanals
05avm/res/data-factory/factory/integration-runtimeData Factory - Integration Runtime (Child of avm/res/data-factory/factory)
(Inherited): TomazMlakar
Tomaz Mlakar
06avm/res/data-factory/factory/linked-serviceData Factory - Linked Service (Child of avm/res/data-factory/factory)
(Inherited): TomazMlakar
Tomaz Mlakar
07avm/res/data-factory/factory/managed-virtual-networkData Factory - Managed Virtual Network (Child of avm/res/data-factory/factory)
(Inherited): TomazMlakar
Tomaz Mlakar
08avm/res/data-factory/factory/managed-virtual-network/managed-private-endpointData Factory - Managed Virtual Network - Managed Private Endpoint (Child of avm/res/data-factory/factory)
(Inherited): TomazMlakar
Tomaz Mlakar
09avm/res/dev-center/devcenter/attachednetworkDev Center - Attachednetwork (Child of avm/res/dev-center/devcenter)
(Inherited): ahmadabdalla
Ahmad Abdalla
10avm/res/dev-center/devcenter/catalogDev Center - Catalog (Child of avm/res/dev-center/devcenter)
(Inherited): ahmadabdalla
Ahmad Abdalla
11avm/res/dev-center/devcenter/devboxdefinitionDev Center - Devboxdefinition (Child of avm/res/dev-center/devcenter)
(Inherited): ahmadabdalla
Ahmad Abdalla
12avm/res/dev-center/devcenter/environment-typeDev Center - Environment Type (Child of avm/res/dev-center/devcenter)
(Inherited): ahmadabdalla
Ahmad Abdalla
13avm/res/dev-center/devcenter/galleryDev Center - Gallery (Child of avm/res/dev-center/devcenter)
(Inherited): ahmadabdalla
Ahmad Abdalla
14avm/res/dev-center/devcenter/project-policyDev Center - Project Policy (Child of avm/res/dev-center/devcenter)
(Inherited): ahmadabdalla
Ahmad Abdalla
15avm/res/dev-center/project/catalogDev Center Project - Catalog (Child of avm/res/dev-center/project)
(Inherited): ahmadabdalla
Ahmad Abdalla
16avm/res/dev-center/project/environment-typeDev Center Project - Environment Type (Child of avm/res/dev-center/project)
(Inherited): ahmadabdalla
Ahmad Abdalla
17avm/res/dev-center/project/poolDev Center Project - Pool (Child of avm/res/dev-center/project)
(Inherited): ahmadabdalla
Ahmad Abdalla
18avm/res/dev-center/project/pool/scheduleDev Center Project - Pool - Schedule (Child of avm/res/dev-center/project)
(Inherited): ahmadabdalla
Ahmad Abdalla
19avm/res/dev-test-lab/lab/artifactsourceDevTest Lab - Artifactsource (Child of avm/res/dev-test-lab/lab)
(Inherited): ahmadabdalla
Ahmad Abdalla
20avm/res/dev-test-lab/lab/costDevTest Lab - Cost (Child of avm/res/dev-test-lab/lab)
(Inherited): ahmadabdalla
Ahmad Abdalla
21avm/res/dev-test-lab/lab/notificationchannelDevTest Lab - Notificationchannel (Child of avm/res/dev-test-lab/lab)
(Inherited): ahmadabdalla
Ahmad Abdalla
22avm/res/dev-test-lab/lab/policyset/policyDevTest Lab - Policyset - Policy (Child of avm/res/dev-test-lab/lab)
(Inherited): ahmadabdalla
Ahmad Abdalla
23avm/res/dev-test-lab/lab/scheduleDevTest Lab - Schedule (Child of avm/res/dev-test-lab/lab)
(Inherited): ahmadabdalla
Ahmad Abdalla
24avm/res/dev-test-lab/lab/secretDevTest Lab - Secret (Child of avm/res/dev-test-lab/lab)
(Inherited): ahmadabdalla
Ahmad Abdalla
25avm/res/dev-test-lab/lab/virtualnetworkDevTest Lab - Virtualnetwork (Child of avm/res/dev-test-lab/lab)
(Inherited): ahmadabdalla
Ahmad Abdalla
26avm/res/digital-twins/digital-twins-instance/endpointDigital Twins Instance - Endpoint (Child of avm/res/digital-twins/digital-twins-instance)
(Inherited): ryanmstephens
Ryan Stephens
27avm/res/event-grid/domain/event-subscriptionEvent Grid Domain - Event Subscription (Child of avm/res/event-grid/domain)
(Inherited): fabmas
Fabio Masciotra
28avm/res/event-grid/domain/topicEvent Grid Domain - Topic (Child of avm/res/event-grid/domain)
(Inherited): fabmas
Fabio Masciotra
29avm/res/event-grid/namespace/ca-certificateEvent Grid Namespace - Ca Certificate (Child of avm/res/event-grid/namespace)
(Inherited): fabmas
Fabio Masciotra
30avm/res/event-grid/namespace/clientEvent Grid Namespace - Client (Child of avm/res/event-grid/namespace)
(Inherited): fabmas
Fabio Masciotra
31avm/res/event-grid/namespace/client-groupEvent Grid Namespace - Client Group (Child of avm/res/event-grid/namespace)
(Inherited): fabmas
Fabio Masciotra
32avm/res/event-grid/namespace/permission-bindingEvent Grid Namespace - Permission Binding (Child of avm/res/event-grid/namespace)
(Inherited): fabmas
Fabio Masciotra
33avm/res/event-grid/namespace/topicEvent Grid Namespace - Topic (Child of avm/res/event-grid/namespace)
(Inherited): fabmas
Fabio Masciotra
34avm/res/event-grid/namespace/topic-spaceEvent Grid Namespace - Topic Space (Child of avm/res/event-grid/namespace)
(Inherited): fabmas
Fabio Masciotra
35avm/res/event-grid/namespace/topic/event-subscriptionEvent Grid Namespace - Topic - Event Subscription (Child of avm/res/event-grid/namespace)
(Inherited): fabmas
Fabio Masciotra
36avm/res/event-grid/topic/event-subscriptionEvent Grid Topic - Event Subscription (Child of avm/res/event-grid/topic)
(Inherited): fabmas
Fabio Masciotra
37avm/res/hybrid-compute/machine/extensionHybrid Compute Machine - Extension (Child of avm/res/hybrid-compute/machine)
(Inherited): chirag1603
Chirag Choudha
38avm/res/logic/integration-account/agreementLogic Apps Integration Account - Agreement (Child of avm/res/logic/integration-account)
(Inherited): lsnoddy
Luke Snoddy
39avm/res/logic/integration-account/assemblyLogic Apps Integration Account - Assembly (Child of avm/res/logic/integration-account)
(Inherited): lsnoddy
Luke Snoddy
40avm/res/logic/integration-account/certificateLogic Apps Integration Account - Certificate (Child of avm/res/logic/integration-account)
(Inherited): lsnoddy
Luke Snoddy
41avm/res/logic/integration-account/mapLogic Apps Integration Account - Map (Child of avm/res/logic/integration-account)
(Inherited): lsnoddy
Luke Snoddy
42avm/res/logic/integration-account/partnerLogic Apps Integration Account - Partner (Child of avm/res/logic/integration-account)
(Inherited): lsnoddy
Luke Snoddy
43avm/res/logic/integration-account/schemaLogic Apps Integration Account - Schema (Child of avm/res/logic/integration-account)
(Inherited): lsnoddy
Luke Snoddy
44avm/res/managed-identity/user-assigned-identity/federated-identity-credentialUser Assigned Identity - Federated Identity Credential (Child of avm/res/managed-identity/user-assigned-identity)
(Inherited): gpacetti
Giuseppe Pacetti
45avm/res/net-app/net-app-account/backup-policyAzure NetApp File - Backup Policy (Child of avm/res/net-app/net-app-account)
(Inherited): fbinotto
Felipe Binotto
46avm/res/net-app/net-app-account/backup-vaultAzure NetApp File - Backup Vault (Child of avm/res/net-app/net-app-account)
(Inherited): fbinotto
Felipe Binotto
47avm/res/net-app/net-app-account/backup-vault/backupAzure NetApp File - Backup Vault - Backup (Child of avm/res/net-app/net-app-account)
(Inherited): fbinotto
Felipe Binotto
48avm/res/net-app/net-app-account/capacity-poolAzure NetApp File - Capacity Pool (Child of avm/res/net-app/net-app-account)
(Inherited): fbinotto
Felipe Binotto
49avm/res/net-app/net-app-account/capacity-pool/volumeAzure NetApp File - Capacity Pool - Volume (Child of avm/res/net-app/net-app-account)
(Inherited): fbinotto
Felipe Binotto
50avm/res/net-app/net-app-account/snapshot-policyAzure NetApp File - Snapshot Policy (Child of avm/res/net-app/net-app-account)
(Inherited): fbinotto
Felipe Binotto
51avm/res/operational-insights/workspace/data-exportLog Analytics Workspace - Data Export (Child of avm/res/operational-insights/workspace)
(Inherited): krbar
Kris Baranek
52avm/res/operational-insights/workspace/data-sourceLog Analytics Workspace - Data Source (Child of avm/res/operational-insights/workspace)
(Inherited): krbar
Kris Baranek
53avm/res/operational-insights/workspace/linked-serviceLog Analytics Workspace - Linked Service (Child of avm/res/operational-insights/workspace)
(Inherited): krbar
Kris Baranek
54avm/res/operational-insights/workspace/linked-storage-accountLog Analytics Workspace - Linked Storage Account (Child of avm/res/operational-insights/workspace)
(Inherited): krbar
Kris Baranek
55avm/res/operational-insights/workspace/saved-searchLog Analytics Workspace - Saved Search (Child of avm/res/operational-insights/workspace)
(Inherited): krbar
Kris Baranek
56avm/res/operational-insights/workspace/storage-insight-configLog Analytics Workspace - Storage Insight Config (Child of avm/res/operational-insights/workspace)
(Inherited): krbar
Kris Baranek
57avm/res/operational-insights/workspace/tableLog Analytics Workspace - Table (Child of avm/res/operational-insights/workspace)
(Inherited): krbar
Kris Baranek
58avm/res/service-networking/traffic-controller/associationApplication Gateway for Containers (Traffic Controller) - Association (Child of avm/res/service-networking/traffic-controller)
(Inherited): krbar
Kris Baranek
59avm/res/service-networking/traffic-controller/frontendApplication Gateway for Containers (Traffic Controller) - Frontend (Child of avm/res/service-networking/traffic-controller)
(Inherited): krbar
Kris Baranek
60avm/res/service-networking/traffic-controller/security-policyApplication Gateway for Containers (Traffic Controller) - Sec Policy (Child of avm/res/service-networking/traffic-controller)
(Inherited): krbar
Kris Baranek

Modules published in April 2026

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/app-configuration/configuration-store/key-valueApp Configuration Store - Key Value (Child of avm/res/app-configuration/configuration-store)
(Inherited): Jfolberth
John Folberth
02avm/res/app-configuration/configuration-store/replicaApp Configuration Store - Replica (Child of avm/res/app-configuration/configuration-store)
(Inherited): Jfolberth
John Folberth
03avm/res/app/container-app/auth-configContainer App - Auth Config (Child of avm/res/app/container-app)
(Inherited): oZakari
Zach Trocinski
04avm/res/cdn/profile/afd-endpointCDN Profile - Afd Endpoint (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
05avm/res/cdn/profile/endpointCDN Profile - Endpoint (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
06avm/res/cdn/profile/endpoint/originCDN Profile - Endpoint - Origin (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
07avm/res/cdn/profile/rule-set/ruleCDN Profile - Rule Set - Rule (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
08avm/res/cdn/profile/secretCDN Profile - Secret (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
09avm/res/data-protection/backup-vault/backup-instanceData Protection Backup Vault - Backup Instance (Child of avm/res/data-protection/backup-vault)
(Inherited): abhishekaryams
Abhishek Arya
10avm/res/data-protection/backup-vault/backup-policyData Protection Backup Vault - Backup Policy (Child of avm/res/data-protection/backup-vault)
(Inherited): abhishekaryams
Abhishek Arya
11avm/res/db-for-postgre-sql/flexible-server/administratorDB for Postgre SQL Flexible Server - Administrator (Child of avm/res/db-for-postgre-sql/flexible-server)
(Inherited): arnoldna
Nate Arnold
12avm/res/db-for-postgre-sql/flexible-server/advanced-threat-protection-settingDB for Postgre SQL Flexible Server - Adv Threat Protection Setting (Child of avm/res/db-for-postgre-sql/flexible-server)
(Inherited): arnoldna
Nate Arnold
13avm/res/db-for-postgre-sql/flexible-server/configurationDB for Postgre SQL Flexible Server - Configuration (Child of avm/res/db-for-postgre-sql/flexible-server)
(Inherited): arnoldna
Nate Arnold
14avm/res/db-for-postgre-sql/flexible-server/databaseDB for Postgre SQL Flexible Server - Database (Child of avm/res/db-for-postgre-sql/flexible-server)
(Inherited): arnoldna
Nate Arnold
15avm/res/db-for-postgre-sql/flexible-server/firewall-ruleDB for Postgre SQL Flexible Server - Firewall Rule (Child of avm/res/db-for-postgre-sql/flexible-server)
(Inherited): arnoldna
Nate Arnold
16avm/res/desktop-virtualization/application-group/applicationAzure Virtual Desktop (AVD) Application Group - Application (Child of avm/res/desktop-virtualization/application-group)
(Inherited):
17avm/res/event-hub/namespace/authorization-ruleEvent Hub Namespace - Authorization Rule (Child of avm/res/event-hub/namespace)
(Inherited): abhishekaryams
Abhishek Arya
18avm/res/event-hub/namespace/disaster-recovery-configEvent Hub Namespace - Disaster Recovery Config (Child of avm/res/event-hub/namespace)
(Inherited): abhishekaryams
Abhishek Arya
19avm/res/event-hub/namespace/eventhub/authorization-ruleEvent Hub Namespace - Eventhub - Authorization Rule (Child of avm/res/event-hub/namespace)
(Inherited): abhishekaryams
Abhishek Arya
20avm/res/event-hub/namespace/eventhub/consumergroupEvent Hub Namespace - Eventhub - Consumergroup (Child of avm/res/event-hub/namespace)
(Inherited): abhishekaryams
Abhishek Arya
21avm/res/event-hub/namespace/network-rule-setEvent Hub Namespace - Network Rule Set (Child of avm/res/event-hub/namespace)
(Inherited): abhishekaryams
Abhishek Arya
22avm/res/healthcare-apis/workspace/dicomserviceHealthcare API Workspace - Dicomservice (Child of avm/res/healthcare-apis/workspace)
(Inherited):
23avm/res/healthcare-apis/workspace/fhirserviceHealthcare API Workspace - Fhirservice (Child of avm/res/healthcare-apis/workspace)
(Inherited):
24avm/res/healthcare-apis/workspace/iotconnectorHealthcare API Workspace - Iotconnector (Child of avm/res/healthcare-apis/workspace)
(Inherited):
25avm/res/healthcare-apis/workspace/iotconnector/fhirdestinationHealthcare API Workspace - Iotconnector - Fhirdestination (Child of avm/res/healthcare-apis/workspace)
(Inherited):
26avm/res/network/virtual-network/virtual-network-peeringVirtual Network - Peering (Child of avm/res/network/virtual-network)
VNET Peering

(Inherited): mjrich19
MJ Richardson
27avm/res/relay/namespace/authorization-ruleRelay Namespace - Authorization Rule (Child of avm/res/relay/namespace)
(Inherited):
28avm/res/relay/namespace/hybrid-connectionRelay Namespace - Hybrid Connection (Child of avm/res/relay/namespace)
(Inherited):
29avm/res/relay/namespace/hybrid-connection/authorization-ruleRelay Namespace - Hybrid Connection - Authorization Rule (Child of avm/res/relay/namespace)
(Inherited):
30avm/res/relay/namespace/network-rule-setRelay Namespace - Network Rule Set (Child of avm/res/relay/namespace)
(Inherited):
31avm/res/relay/namespace/wcf-relayRelay Namespace - Wcf Relay (Child of avm/res/relay/namespace)
(Inherited):
32avm/res/relay/namespace/wcf-relay/authorization-ruleRelay Namespace - Wcf Relay - Authorization Rule (Child of avm/res/relay/namespace)
(Inherited):

Modules published in March 2026

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/cdn/profile/afd-endpoint/routeCDN Profile - AFD Endpoint Route (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
02avm/res/cdn/profile/custom-domainCDN Profile - Custom Domain (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
03avm/res/cdn/profile/origin-groupCDN Profile - Origin Group (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
04avm/res/cdn/profile/origin-group/originCDN Profile - Origin Group - Origin (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
05avm/res/cdn/profile/rule-setCDN Profile - Rule Set (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
06avm/res/cdn/profile/security-policyCDN Profile - Security Policy (Child of avm/res/cdn/profile)
(Inherited): gbeaud
Guillaume Beaud
07avm/res/container-registry/registry/cache-ruleAzure Container Registry (ACR) - Cache Rule (Child of avm/res/container-registry/registry)
(Inherited): JPEasier
Julian Peißker
08avm/res/container-registry/registry/credential-setAzure Container Registry (ACR) - Credential Set (Child of avm/res/container-registry/registry)
(Inherited): JPEasier
Julian Peißker
09avm/res/container-registry/registry/replicationAzure Container Registry (ACR) - Replication (Child of avm/res/container-registry/registry)
(Inherited): JPEasier
Julian Peißker
10avm/res/container-registry/registry/scope-mapAzure Container Registry (ACR) - Scope Map (Child of avm/res/container-registry/registry)
(Inherited): JPEasier
Julian Peißker
11avm/res/container-registry/registry/taskAzure Container Registry (ACR) - Task (Child of avm/res/container-registry/registry)
(Inherited): JPEasier
Julian Peißker
12avm/res/container-registry/registry/tokenAzure Container Registry (ACR) - Token (Child of avm/res/container-registry/registry)
(Inherited): JPEasier
Julian Peißker
13avm/res/container-registry/registry/webhookAzure Container Registry (ACR) - Webhook (Child of avm/res/container-registry/registry)
(Inherited): JPEasier
Julian Peißker
14avm/res/devices/iot-hubIoT HubTomazMlakar
Tomaz Mlakar
15avm/res/network/private-dns-zone/virtual-network-linkVirtual Network Links (Child of avm/res/network/private-dns-zone)
VNET Link

(Inherited): ChrisSidebotham
Chris Sidebotham
16avm/res/storage/storage-account/blob-service/container/immutability-policyStorage Account - Blob Container Immutability Policy (Child of avm/res/storage/storage-account)
(Inherited): fblix
Felix Borst
17avm/res/storage/storage-account/local-userStorage Account - Local User (Child of avm/res/storage/storage-account)
(Inherited): fblix
Felix Borst
18avm/res/storage/storage-account/management-policyStorage Account - Management Policy (Child of avm/res/storage/storage-account)
(Inherited): fblix
Felix Borst
19avm/res/storage/storage-account/queue-service/queueStorage Account - Queue (Child of avm/res/storage/storage-account)
(Inherited): fblix
Felix Borst
20avm/res/storage/storage-account/table-service/tableStorage Account - Table (Child of avm/res/storage/storage-account)
(Inherited): fblix
Felix Borst

Modules published in January 2026

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/service-bus/namespace/queueService Bus Namespace - Queue (Child of avm/res/service-bus/namespace)
(Inherited): ChrisSidebotham
Chris Sidebotham
02avm/res/service-bus/namespace/topicService Bus Namespace - Topic (Child of avm/res/service-bus/namespace)
(Inherited): ChrisSidebotham
Chris Sidebotham

Modules published in December 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/logic/integration-accountLogic Apps Integration Accountlsnoddy
Luke Snoddy

Modules published in November 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/operational-insights/clusterLog Analytics Dedicated Clusterabhishekaryams
Abhishek Arya

Modules published in September 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/api-management/service/apiAPI Management Service - API (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
02avm/res/api-management/service/api-version-setAPI Management Service - API Version Set (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
03avm/res/api-management/service/api/diagnosticsAPI Management Service - API Diagnostics (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
04avm/res/api-management/service/api/policyAPI Management Service - API Policy (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
05avm/res/api-management/service/authorization-serverAPI Management Service - Authorization Server (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
06avm/res/api-management/service/backendAPI Management Service - Backend (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
07avm/res/api-management/service/cacheAPI Management Service - Cache (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
08avm/res/api-management/service/identity-providerAPI Management Service - Identity Provider (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
09avm/res/api-management/service/loggerAPI Management Service - Logger (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
10avm/res/api-management/service/named-valueAPI Management Service - Named Value (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
11avm/res/api-management/service/policyAPI Management Service - Policy (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
12avm/res/api-management/service/portalsettingAPI Management Service - Portal Setting (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
13avm/res/api-management/service/productAPI Management Service - Product (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
14avm/res/api-management/service/product/apiAPI Management Service - Product API (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
15avm/res/api-management/service/product/groupAPI Management Service - Product Group (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
16avm/res/api-management/service/subscriptionAPI Management Service - Subscription (Child of avm/res/api-management/service)
(Inherited): abhishekaryams
Abhishek Arya
17avm/res/authorization/policy-assignment/mg-scopeAuthorization - Policy Assignment - Management Group Scope (Child of avm/res/authorization/policy-assignment)
(Inherited): AlexanderSehr
Alexander Sehr
18avm/res/authorization/policy-assignment/rg-scopeAuthorization - Policy Assignment - Resource Group Scope (Child of avm/res/authorization/policy-assignment)
(Inherited): AlexanderSehr
Alexander Sehr
19avm/res/authorization/policy-assignment/sub-scopeAuthorization - Policy Assignment - Subscription Scope (Child of avm/res/authorization/policy-assignment)
(Inherited): AlexanderSehr
Alexander Sehr
20avm/res/consumption/budget/mg-scopeConsumption Budget - Management Group Scope (Child of avm/res/consumption/budget)
(Inherited): segraef
Sebastian Graef
21avm/res/consumption/budget/rg-scopeConsumption Budget - Resource Group Scope (Child of avm/res/consumption/budget)
(Inherited): segraef
Sebastian Graef
22avm/res/consumption/budget/sub-scopeConsumption Budget - Subscription Scope (Child of avm/res/consumption/budget)
(Inherited): segraef
Sebastian Graef
23avm/res/document-db/database-account/sql-role-assignmentCosmos DB - SQL Role Assignment (Child of avm/res/document-db/database-account)
(Inherited): cmaneu
Christopher Maneu
24avm/res/document-db/database-account/sql-role-definitionCosmos DB - SQL Role Definition (Child of avm/res/document-db/database-account)
(Inherited): cmaneu
Christopher Maneu
25avm/res/event-hub/namespace/eventhubEvent Hub (Child of avm/res/event-hub/namespace)
(Inherited): abhishekaryams
Abhishek Arya
26avm/res/kubernetes-runtime/load-balancerKubernetes Runtime Load Balancerchirag1603
Chirag Choudha
27avm/res/management/service-groupService Groupjtracey93
Jack Tracey
28avm/res/sql/server/databaseAzure SQL Database (Child of avm/res/sql/server)
(Inherited): peterbud
Peter Budai

Modules published in August 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/key-vault/vault/access-policyKey Vault - Access Policy (Child of avm/res/key-vault/vault)
(Inherited): fblix
Felix Borst
02avm/res/key-vault/vault/keyKey Vault - Key (Child of avm/res/key-vault/vault)
(Inherited): fblix
Felix Borst
03avm/res/key-vault/vault/secretKey Vault - Secret (Child of avm/res/key-vault/vault)
(Inherited): fblix
Felix Borst
04avm/res/network/private-dns-zone/aPrivate DNS Zone - A (Child of avm/res/network/private-dns-zone)
(Inherited): ChrisSidebotham
Chris Sidebotham
05avm/res/network/private-dns-zone/aaaaPrivate DNS Zone - AAAA (Child of avm/res/network/private-dns-zone)
(Inherited): ChrisSidebotham
Chris Sidebotham
06avm/res/network/private-dns-zone/cnamePrivate DNS Zone - CNAME (Child of avm/res/network/private-dns-zone)
(Inherited): ChrisSidebotham
Chris Sidebotham
07avm/res/network/private-dns-zone/mxPrivate DNS Zone - MX (Child of avm/res/network/private-dns-zone)
(Inherited): ChrisSidebotham
Chris Sidebotham
08avm/res/network/private-dns-zone/ptrPrivate DNS Zone - PTR (Child of avm/res/network/private-dns-zone)
(Inherited): ChrisSidebotham
Chris Sidebotham
09avm/res/network/private-dns-zone/soaPrivate DNS Zone - SOA (Child of avm/res/network/private-dns-zone)
(Inherited): ChrisSidebotham
Chris Sidebotham
10avm/res/network/private-dns-zone/srvPrivate DNS Zone - SRV (Child of avm/res/network/private-dns-zone)
(Inherited): ChrisSidebotham
Chris Sidebotham
11avm/res/network/private-dns-zone/txtPrivate DNS Zone - TXT (Child of avm/res/network/private-dns-zone)
(Inherited): ChrisSidebotham
Chris Sidebotham
12avm/res/web/site/configWeb Site Configuration (Child of avm/res/web/site)
(Inherited): tsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal
13avm/res/web/site/slotWeb Site Slot (Child of avm/res/web/site)
(Inherited): tsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal

Modules published in July 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/azure-stack-hci/marketplace-gallery-imageAzure Stack HCI Marketplace Gallery Imagechirag1603
Chirag Choudha
02avm/res/azure-stack-hci/virtual-machine-instanceAzure Stack HCI Virtual Machine Instancechirag1603
Chirag Choudha
03avm/res/storage/storage-account/file-service/shareStorage Account - File Share (Child of avm/res/storage/storage-account)
(Inherited): fblix
Felix Borst

Modules published in June 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/authorization/role-assignment/mg-scopeAuthorization - Role Assignment - Management Group Scope (Child of avm/res/authorization/role-assignment)
(Inherited): arnoldna
Nate Arnold
02avm/res/authorization/role-assignment/rg-scopeAuthorization - Role Assignment - Resource Group Scope (Child of avm/res/authorization/role-assignment)
(Inherited): arnoldna
Nate Arnold
03avm/res/authorization/role-assignment/sub-scopeAuthorization - Role Assignment - Subscription Scope (Child of avm/res/authorization/role-assignment)
(Inherited): arnoldna
Nate Arnold
04avm/res/dev-center/devcenterDev Centerahmadabdalla
Ahmad Abdalla
05avm/res/dev-center/projectDev Center Projectahmadabdalla
Ahmad Abdalla
06avm/res/machine-learning-services/registryMachine Learning Services Registryjosunefon
Jordi Sune Fontanals
07avm/res/storage/storage-account/blob-service/containerStorage Account - Blob Container (Child of avm/res/storage/storage-account)
(Inherited): fblix
Felix Borst

Modules published in May 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/dev-center/network-connectionDev Center Network Connectionahmadabdalla
Ahmad Abdalla
02avm/res/security-insights/data-connectorSecurity Insights - Data Connector
03avm/res/security-insights/settingSecurity Insights - SettingTomazMlakar
Tomaz Mlakar

Modules published in March 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/azure-stack-hci/network-interfaceAzure Stack HCI Network Interfacechirag1603
Chirag Choudha
02avm/res/azure-stack-hci/virtual-hard-diskAzure Stack HCI Hard Diskchirag1603
Chirag Choudha
03avm/res/hybrid-container-service/provisioned-cluster-instanceHybrid Container Service - Provisioned Cluster Instancechirag1603
Chirag Choudha
04avm/res/kubernetes/connected-clusterKubernetes Connected Clusterchirag1603
Chirag Choudha
05avm/res/maintenance/configuration-assignmentMaintenance Configuration Assignmenteriqua
Erika Gressi
06avm/res/maps/accountAzure Maps Accountjhueppauff
Julian Huppauff
07avm/res/network/network-security-perimeterNetwork Security Perimeterpeterbud
Peter Budai
08avm/res/network/virtual-network/subnetVirtual Network - Subnet (Child of avm/res/network/virtual-network)
(Inherited): mjrich19
MJ Richardson

Modules published in February 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/app/session-poolApp Session Poolabhishekaryams
Abhishek Arya
02avm/res/azure-stack-hci/clusterAzure Stack HCI Clusterchirag1603
Chirag Choudha
03avm/res/azure-stack-hci/logical-networkAzure Stack HCI Logical Networkchirag1603
Chirag Choudha
04avm/res/cache/redis-enterpriseRedis Enterprise CacheJeffreyCA
Jeffrey Chen
05avm/res/hybrid-compute/gatewayHybrid Compute Gatewaychirag1603
Chirag Choudha
06avm/res/hybrid-compute/licenseHybrid Compute Licensechirag1603
Chirag Choudha

Modules published in January 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/network/express-route-portExpressRoute Port
ER Port
arnoldna
Nate Arnold

Modules published in December 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/elastic-san/elastic-sanElastic SAN
SAN, ESAN, Elastic SAN, Azure Elastic Storage Area Network, iSCSI, internet Small Computer Systems Interface
jbinko
Jiri Binko
02avm/res/network/p2s-vpn-gatewayP2S VPN Gatewayericscheffler
Eric Scheffler

Modules published in October 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/fabric/capacityFabricmswantek68
Mike Swantek
cmaneu
Christopher Maneu
02avm/res/network/vpn-server-configurationVPN Server Configurationericscheffler
Eric Scheffler
03avm/res/service-networking/traffic-controllerApplication Gateway for Containers (Traffic Controller)krbar
Kris Baranek

Modules published in September 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/dev-ops-infrastructure/poolDevOps Infrastructure Poolelizatargithub7
Eliza Tarasila

Modules published in June 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/alerts-management/action-ruleAction Rulesjudyer28
Justin Dyer
02avm/res/hybrid-compute/machineHybrid Compute Machinechirag1603
Chirag Choudha
03avm/res/kusto/clusterAzure Data Explorer (Kusto) clusteroZakari
Zach Trocinski
04avm/res/portal/dashboardPortal Dashboardkrbar
Kris Baranek

Modules published in May 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/app/jobApp JobReneHezser
Rene Hezser
02avm/res/communication/communication-serviceCommunication Servicedonk-msft
Don Koning
03avm/res/communication/email-serviceEmail Communication Servicedonk-msft
Don Koning

Modules published in April 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/aad/domain-serviceAzure Active Directory Domain Service
AAD, Entra ID, Microsoft Entra Domain Services, AAD DS, Azure AD DS
ReneHezser
Rene Hezser
CRYP70N1X
Paul Chirila
02avm/res/healthcare-apis/workspaceHealthcare API Workspace
03avm/res/load-test-service/load-testLoad Testing Servicesebassem
Seif Bassem
04avm/res/managed-services/registration-definitionRegistration Definition (Lighthouse)
05avm/res/network/application-gatewayApplication Gateway
App GW
toddbeauchemin
Todd Beauchemin
06avm/res/network/application-gateway-web-application-firewall-policyApplication Gateway Web Application Firewall (WAF) Policytoddbeauchemin
Todd Beauchemin
07avm/res/network/network-watcherNetwork Watchersegraef
Sebastian Graef
08avm/res/service-fabric/clusterService Fabric Clusterlsnoddy
Luke Snoddy
09avm/res/sql/instance-poolSQL Instance Poolgpacetti
Giuseppe Pacetti
10avm/res/sql/managed-instanceSQL Managed Instance
SQL MI
chanakanissanka
Chanaka Nissanka

Modules published in March 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/app-configuration/configuration-storeApp Configuration StoreJfolberth
John Folberth
02avm/res/cdn/profileCDN Profile
Azure Front Door
gbeaud
Guillaume Beaud
03avm/res/compute/virtual-machine-scale-setVirtual Machine Scale Set
VMSS
josunefon
Jordi Sune Fontanals
04avm/res/container-instance/container-groupContainer Instance
ACI
JPEasier
Julian Peißker
05avm/res/digital-twins/digital-twins-instanceDigital Twins Instanceryanmstephens
Ryan Stephens
06avm/res/event-grid/namespaceEvent Grid Namespacefabmas
Fabio Masciotra
07avm/res/event-hub/namespaceEvent Hub Namespaceabhishekaryams
Abhishek Arya
08avm/res/network/azure-firewallAzure Firewall
Azure FW
jtracey93
Jack Tracey
oZakari
Zach Trocinski
09avm/res/network/service-endpoint-policyService Endpoint Policyjeetgarg
Jeet Garg
10avm/res/recovery-services/vaultRecovery Services Vaultalexanderojala
Alexander Ojala
11avm/res/relay/namespaceRelay Namespace
12avm/res/signal-r-service/signal-rSignalR Service SignalR
13avm/res/signal-r-service/web-pub-subSignalR Web PubSub Service
14avm/res/web/connectionAPI Connectionabhishekaryams
Abhishek Arya
15avm/res/web/hosting-environmentApp Service Environment
ASE
tsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal

Modules published in February 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/compute/availability-setAvailability Set
AS
ahmadabdalla
Ahmad Abdalla
02avm/res/desktop-virtualization/application-groupAzure Virtual Desktop (AVD) Application Group
03avm/res/desktop-virtualization/host-poolAzure Virtual Desktop (AVD) Host Pool
04avm/res/desktop-virtualization/scaling-planAzure Virtual Desktop (AVD) Scaling Plan
05avm/res/desktop-virtualization/workspaceAzure Virtual Desktop (AVD) Workspacereikor0x
Vincenzo Sciarra
06avm/res/dev-test-lab/labDevTest Labahmadabdalla
Ahmad Abdalla
07avm/res/insights/private-link-scopeAzure Monitor Private Link Scopeahmadabdalla
Ahmad Abdalla
08avm/res/machine-learning-services/workspaceMachine Learning Services Workspace
ML Workspace
cecheta
Chinedum Echeta
ross-p-smith
Ross Smith
09avm/res/management/management-groupManagement Group
MG
fblix
Felix Borst
10avm/res/network/ip-groupIP Groupahmadabdalla
Ahmad Abdalla
11avm/res/network/network-managerNetwork Managerahmadabdalla
Ahmad Abdalla
12avm/res/network/private-link-servicePrivate Link Serviceahmadabdalla
Ahmad Abdalla
13avm/res/network/virtual-hubVirtual Hubarnoldna
Nate Arnold
14avm/res/network/virtual-wanVirtual WAN
vWAN
arnoldna
Nate Arnold
15avm/res/purview/accountPurview Accountabhishekaryams
Abhishek Arya
16avm/res/synapse/private-link-hubAzure Synapse Analytics Private Link HubTomazMlakar
Tomaz Mlakar
17avm/res/synapse/workspaceAzure Synapse Analytics WorkspaceTomazMlakar
Tomaz Mlakar
18avm/res/virtual-machine-images/image-templateVirtual Machine Image Templateahmadabdalla
Ahmad Abdalla

Modules published in January 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/analysis-services/serverAnalysis Services Server
02avm/res/app/container-appContainer AppoZakari
Zach Trocinski
03avm/res/cache/redisRedis CacheTomazMlakar
Tomaz Mlakar
04avm/res/compute/diskCompute Disksegraef
Sebastian Graef
05avm/res/compute/disk-encryption-setDisk Encryption Setsegraef
Sebastian Graef
06avm/res/compute/imageImagetony-box
Tony Box
07avm/res/compute/proximity-placement-groupProximity Placement Groupjeetgarg
Jeet Garg
08avm/res/compute/virtual-machineVirtual Machine
VM
josunefon
Jordi Sune Fontanals
09avm/res/consumption/budgetConsumption Budgetsegraef
Sebastian Graef
10avm/res/container-registry/registryAzure Container Registry (ACR)JPEasier
Julian Peißker
11avm/res/container-service/managed-clusterAzure Kubernetes Service (AKS) Managed ClusterJPEasier
Julian Peißker
12avm/res/data-protection/backup-vaultData Protection Backup Vaultabhishekaryams
Abhishek Arya
13avm/res/databricks/access-connectorAzure Databricks Access Connectorabhishekaryams
Abhishek Arya
14avm/res/databricks/workspaceAzure Databricks Workspaceabhishekaryams
Abhishek Arya
15avm/res/db-for-my-sql/flexible-serverDB for MySQL Flexible Serverabhishekaryams
Abhishek Arya
16avm/res/health-bot/health-botAzure Health BotTomazMlakar
Tomaz Mlakar
17avm/res/net-app/net-app-accountAzure NetApp Filefbinotto
Felipe Binotto
18avm/res/network/ddos-protection-planDDoS Protection Plansegraef
Sebastian Graef
19avm/res/network/firewall-policyFirewall Policyjtracey93
Jack Tracey
oZakari
Zach Trocinski
20avm/res/network/front-doorAzure Front Door
21avm/res/network/front-door-web-application-firewall-policyFront Door Web Application Firewall (WAF) PolicyPaulJohnston88
Paul Johnston
22avm/res/network/local-network-gatewayLocal Network Gatewayfabmas
Fabio Masciotra
23avm/res/network/nat-gatewayNAT Gateway
NAT GW
fabmas
Fabio Masciotra
24avm/res/network/virtual-network-gatewayVirtual Network Gateway
VNET GW
fabmas
Fabio Masciotra
25avm/res/network/vpn-gatewayVPN Gateway
VPN GW
fabmas
Fabio Masciotra
26avm/res/storage/storage-accountStorage Accountfblix
Felix Borst
27avm/res/web/siteWeb/Function App
App Service, Web Site, Logic App, Function App
tsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal
28avm/res/web/static-siteStatic Web AppChrisSidebotham
Chris Sidebotham

Modules published in December 2023

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/api-management/serviceAPI Management Serviceabhishekaryams
Abhishek Arya
02avm/res/app/managed-environmentApp Managed Environmentabhishekaryams
Abhishek Arya
03avm/res/automation/automation-accountAutomation Accountgpacetti
Giuseppe Pacetti
04avm/res/compute/galleryAzure Compute GalleryReneHezser
Rene Hezser
05avm/res/data-factory/factoryData FactoryTomazMlakar
Tomaz Mlakar
06avm/res/document-db/database-accountCosmos DB Database Accountcmaneu
Christopher Maneu
07avm/res/insights/activity-log-alertActivity Log Alertdonk-msft
Don Koning
08avm/res/insights/data-collection-endpointData Collection Endpointkrbar
Kris Baranek
09avm/res/insights/data-collection-ruleData Collection Rule
DCR
krbar
Kris Baranek
10avm/res/insights/metric-alertMetric Alertkijunkang
Ki Jun Kang
11avm/res/insights/scheduled-query-ruleScheduled Query Ruleabhishekaryams
Abhishek Arya
12avm/res/insights/webtestWeb TestJfolberth
John Folberth
13avm/res/maintenance/maintenance-configurationMaintenance Configurationarievanderwende
Arie van der Wende
14avm/res/managed-identity/user-assigned-identityUser Assigned Identity
MSI
gpacetti
Giuseppe Pacetti
15avm/res/network/application-security-groupApplication Security Group (ASG)
ASG
segraef
Sebastian Graef
16avm/res/network/bastion-hostBastion Hostkrbar
Kris Baranek
17avm/res/network/connectionVirtual Network Gateway Connectionfabmas
Fabio Masciotra
18avm/res/network/network-security-groupNetwork Security Group
NSG
ahmadabdalla
Ahmad Abdalla
19avm/res/network/public-ip-prefixPublic IP Prefix
PIP Prefix
krbar
Kris Baranek
20avm/res/network/trafficmanagerprofileTraffic Manager Profilelsnoddy
Luke Snoddy
21avm/res/network/vpn-siteVPN Sitefabmas
Fabio Masciotra
22avm/res/resources/resource-groupResource Group
RG
segraef
Sebastian Graef
23avm/res/service-bus/namespaceService Bus NamespaceChrisSidebotham
Chris Sidebotham
24avm/res/web/serverfarmApp Service Plantsc-buddy
Buddy Davies
pankajagrawal16
Pankaj Agrawal

Modules published in November 2023

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/batch/batch-accountBatch Accountdidayal-msft
Divyadeep Dayal
02avm/res/db-for-postgre-sql/flexible-serverDB for Postgre SQL Flexible Serverarnoldna
Nate Arnold
03avm/res/event-grid/domainEvent Grid Domainfabmas
Fabio Masciotra
04avm/res/event-grid/system-topicEvent Grid System Topicfabmas
Fabio Masciotra
05avm/res/event-grid/topicEvent Grid Topicfabmas
Fabio Masciotra
06avm/res/insights/componentApplication Insightkrbar
Kris Baranek
07avm/res/insights/diagnostic-settingDiagnostic Settingkrbar
Kris Baranek
08avm/res/logic/workflowLogic Apps Workflowlsnoddy
Luke Snoddy
09avm/res/network/express-route-circuitExpressRoute Circuit
ER Circuit
arnoldna
Nate Arnold
10avm/res/network/express-route-gatewayExpress Route Gateway
ER GW
arnoldna
Nate Arnold
11avm/res/network/load-balancerLoad Balancer
LB, NLB
arnoldna
Nate Arnold
12avm/res/network/route-tableRoute Table
UDR
segraef
Sebastian Graef
13avm/res/network/virtual-networkVirtual Network
VNET
mjrich19
MJ Richardson
14avm/res/operational-insights/workspaceLog Analytics Workspacekrbar
Kris Baranek
15avm/res/operations-management/solutionOperations Management Solutionkrbar
Kris Baranek
16avm/res/power-bi-dedicated/capacityPower BI Dedicated CapacityChrisSidebotham
Chris Sidebotham
17avm/res/resource-graph/queryResource Graph Querysebassem
Seif Bassem
18avm/res/resources/deployment-scriptDeployment Scriptsebassem
Seif Bassem
19avm/res/search/search-serviceSearch Servicekrbar
Kris Baranek
20avm/res/sql/serverAzure SQL Serverpeterbud
Peter Budai

Modules published in October 2023

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/res/cognitive-services/accountAzure AI Services (Cognitive Services)
AI Foundry
mswantek68
Mike Swantek
chanakanissanka
Chanaka Nissanka
02avm/res/compute/ssh-public-keyPublic SSH KeyChrisSidebotham
Chris Sidebotham
03avm/res/document-db/mongo-clusterCosmos DB for MongoDB (vCore)sinedied
Yohan Lasorsa
04avm/res/insights/action-groupAction Grouprahalan
Rainer Halanek
05avm/res/key-vault/vaultKey Vault
KV
fblix
Felix Borst
06avm/res/kubernetes-configuration/extensionKubernetes Configuration ExtensionJPEasier
Julian Peißker
07avm/res/kubernetes-configuration/flux-configurationKubernetes Configuration Flux ConfigurationJPEasier
Julian Peißker
08avm/res/network/dns-forwarding-rulesetDNS Forwarding RulesetChrisSidebotham
Chris Sidebotham
09avm/res/network/dns-resolverDNS ResolverChrisSidebotham
Chris Sidebotham
10avm/res/network/dns-zonePublic DNS ZoneChrisSidebotham
Chris Sidebotham
11avm/res/network/network-interfaceNetwork Interface
NIC
rahalan
Rainer Halanek
12avm/res/network/private-dns-zonePrivate DNS ZoneChrisSidebotham
Chris Sidebotham
13avm/res/network/private-endpointPrivate Endpointsegraef
Sebastian Graef
14avm/res/network/public-ip-addressPublic IP Address
PIP
ChrisSidebotham
Chris Sidebotham
krbar
Kris Baranek

Consistent Features & Extension Resources (Interfaces)

➕ Consistent Features & Extension Resources (Interfaces)

The following table shows which Bicep resource modules have which consistent features and extension resources (interfaces) implemented as defined in the Bicep Interfaces specification.

#ModuleRBACLocksTagsDiagPECMKCMK-mHSMIdentity
1avm/res/aad/domain-service
2avm/res/alerts-management/action-rule
3avm/res/analysis-services/server
4avm/res/api-management/service
5avm/res/app-configuration/configuration-store
6avm/res/app/container-app
7avm/res/app/job
8avm/res/app/managed-environment
9avm/res/app/session-pool
10avm/res/authorization/policy-assignment
11avm/res/authorization/role-assignment
12avm/res/automation/automation-account
13avm/res/azure-stack-hci/cluster
14avm/res/azure-stack-hci/logical-network
15avm/res/azure-stack-hci/marketplace-gallery-image
16avm/res/azure-stack-hci/network-interface
17avm/res/azure-stack-hci/virtual-hard-disk
18avm/res/azure-stack-hci/virtual-machine-instance
19avm/res/batch/batch-account
20avm/res/cache/redis
#ModuleRBACLocksTagsDiagPECMKCMK-mHSMIdentity
21avm/res/cache/redis-enterprise
22avm/res/cdn/profile
23avm/res/cognitive-services/account
24avm/res/communication/communication-service
25avm/res/communication/email-service
26avm/res/compute/availability-set
27avm/res/compute/disk
28avm/res/compute/disk-encryption-set
29avm/res/compute/gallery
30avm/res/compute/image
31avm/res/compute/proximity-placement-group
32avm/res/compute/ssh-public-key
33avm/res/compute/virtual-machine
34avm/res/compute/virtual-machine-scale-set
35avm/res/consumption/budget
36avm/res/container-instance/container-group
37avm/res/container-registry/registry
38avm/res/container-service/managed-cluster
39avm/res/data-factory/factory
40avm/res/data-protection/backup-vault
#ModuleRBACLocksTagsDiagPECMKCMK-mHSMIdentity
41avm/res/databricks/access-connector
42avm/res/databricks/workspace
43avm/res/db-for-my-sql/flexible-server
44avm/res/db-for-postgre-sql/flexible-server
45avm/res/desktop-virtualization/application-group
46avm/res/desktop-virtualization/host-pool
47avm/res/desktop-virtualization/scaling-plan
48avm/res/desktop-virtualization/workspace
49avm/res/dev-center/devcenter
50avm/res/dev-center/network-connection
51avm/res/dev-center/project
52avm/res/dev-ops-infrastructure/pool
53avm/res/dev-test-lab/lab
54avm/res/devices/iot-hub
55avm/res/digital-twins/digital-twins-instance
56avm/res/document-db/database-account
57avm/res/document-db/mongo-cluster
58avm/res/elastic-san/elastic-san
59avm/res/event-grid/domain
60avm/res/event-grid/namespace
#ModuleRBACLocksTagsDiagPECMKCMK-mHSMIdentity
61avm/res/event-grid/system-topic
62avm/res/event-grid/topic
63avm/res/event-hub/namespace
64avm/res/fabric/capacity
65avm/res/health-bot/health-bot
66avm/res/healthcare-apis/workspace
67avm/res/hybrid-compute/gateway
68avm/res/hybrid-compute/license
69avm/res/hybrid-compute/machine
70avm/res/hybrid-container-service/provisioned-cluster-instance
71avm/res/insights/action-group
72avm/res/insights/activity-log-alert
73avm/res/insights/component
74avm/res/insights/data-collection-endpoint
75avm/res/insights/data-collection-rule
76avm/res/insights/diagnostic-setting
77avm/res/insights/metric-alert
78avm/res/insights/private-link-scope
79avm/res/insights/scheduled-query-rule
80avm/res/insights/webtest
#ModuleRBACLocksTagsDiagPECMKCMK-mHSMIdentity
81avm/res/key-vault/vault
82avm/res/kubernetes-configuration/extension
83avm/res/kubernetes-configuration/flux-configuration
84avm/res/kubernetes-runtime/load-balancer
85avm/res/kubernetes/connected-cluster
86avm/res/kusto/cluster
87avm/res/load-test-service/load-test
88avm/res/logic/integration-account
89avm/res/logic/workflow
90avm/res/machine-learning-services/registry
91avm/res/machine-learning-services/workspace
92avm/res/maintenance/configuration-assignment
93avm/res/maintenance/maintenance-configuration
94avm/res/managed-identity/user-assigned-identity
95avm/res/managed-services/registration-definition
96avm/res/management/management-group
97avm/res/management/service-group
98avm/res/maps/account
99avm/res/net-app/net-app-account
100avm/res/network/application-gateway
#ModuleRBACLocksTagsDiagPECMKCMK-mHSMIdentity
101avm/res/network/application-gateway-web-application-firewall-policy
102avm/res/network/application-security-group
103avm/res/network/azure-firewall
104avm/res/network/bastion-host
105avm/res/network/connection
106avm/res/network/ddos-protection-plan
107avm/res/network/dns-forwarding-ruleset
108avm/res/network/dns-resolver
109avm/res/network/dns-zone
110avm/res/network/express-route-circuit
111avm/res/network/express-route-gateway
112avm/res/network/express-route-port
113avm/res/network/firewall-policy
114avm/res/network/front-door
115avm/res/network/front-door-web-application-firewall-policy
116avm/res/network/ip-group
117avm/res/network/load-balancer
118avm/res/network/local-network-gateway
119avm/res/network/nat-gateway
120avm/res/network/network-interface
#ModuleRBACLocksTagsDiagPECMKCMK-mHSMIdentity
121avm/res/network/network-manager
122avm/res/network/network-security-group
123avm/res/network/network-security-perimeter
124avm/res/network/network-watcher
125avm/res/network/p2s-vpn-gateway
126avm/res/network/private-dns-zone
127avm/res/network/private-endpoint
128avm/res/network/private-link-service
129avm/res/network/public-ip-address
130avm/res/network/public-ip-prefix
131avm/res/network/route-table
132avm/res/network/service-endpoint-policy
133avm/res/network/trafficmanagerprofile
134avm/res/network/virtual-hub
135avm/res/network/virtual-network
136avm/res/network/virtual-network-gateway
137avm/res/network/virtual-wan
138avm/res/network/vpn-gateway
139avm/res/network/vpn-server-configuration
140avm/res/network/vpn-site
#ModuleRBACLocksTagsDiagPECMKCMK-mHSMIdentity
141avm/res/operational-insights/cluster
142avm/res/operational-insights/workspace
143avm/res/operations-management/solution
144avm/res/portal/dashboard
145avm/res/power-bi-dedicated/capacity
146avm/res/purview/account
147avm/res/recovery-services/vault
148avm/res/relay/namespace
149avm/res/resource-graph/query
150avm/res/resources/deployment-script
151avm/res/resources/resource-group
152avm/res/search/search-service
153avm/res/security-insights/data-connector
154avm/res/security-insights/setting
155avm/res/service-bus/namespace
156avm/res/service-fabric/cluster
157avm/res/service-networking/traffic-controller
158avm/res/signal-r-service/signal-r
159avm/res/signal-r-service/web-pub-sub
160avm/res/sql/instance-pool
#ModuleRBACLocksTagsDiagPECMKCMK-mHSMIdentity
161avm/res/sql/managed-instance
162avm/res/sql/server
163avm/res/storage/storage-account
164avm/res/synapse/private-link-hub
165avm/res/synapse/workspace
166avm/res/virtual-machine-images/image-template
167avm/res/web/connection
168avm/res/web/hosting-environment
169avm/res/web/serverfarm
170avm/res/web/site
171avm/res/web/static-site
Sum1481461566840261767

For Module Owners & Contributors

Note

This section is mainly intended for module owners and contributors as it contains information important for module development, such as telemetry ID prefix, and GitHub Teams for Owners.

Module name, Telemetry ID prefix, GitHub Teams for Owners

➕ All Modules - Module name, Telemetry ID prefix, GitHub Teams for Owners
No.Module NameTelemetry ID prefixGitHub Teams for Module Owners (@Azure org)
01avm/res/aad/domain-service46d3xbcp.res.aad-domainserviceavm-res-aad-domainservice-module-owners-bicep
02avm/res/alerts-management/action-rule46d3xbcp.res.alertsmanagement-actionruleavm-res-alertsmanagement-actionrule-module-owners-bicep
03avm/res/analysis-services/server46d3xbcp.res.analysisservices-serveravm-res-analysisservices-server-module-owners-bicep
04avm/res/api-center/service46d3xbcp.res.apicenter-serviceavm-res-apicenter-service-module-owners-bicep
05avm/res/api-management/service46d3xbcp.res.apimanagement-serviceavm-res-apimanagement-service-module-owners-bicep
06avm/res/api-management/service/api46d3xbcp.res.apimgmt-apiavm-res-apimanagement-service-module-owners-bicep
07avm/res/api-management/service/api-version-set46d3xbcp.res.apimgmt-apiversionsetavm-res-apimanagement-service-module-owners-bicep
08avm/res/api-management/service/api/diagnostics46d3xbcp.res.apimgm-apidiagnosticsavm-res-apimanagement-service-module-owners-bicep
09avm/res/api-management/service/api/operation46d3xbcp.res.apimgmt-service-apioperationavm-res-apimanagement-service-module-owners-bicep
10avm/res/api-management/service/api/operation/policy46d3xbcp.res.apimgmt-service-apioperationpolicyavm-res-apimanagement-service-module-owners-bicep
11avm/res/api-management/service/api/policy46d3xbcp.res.apimgmt-apipolicyavm-res-apimanagement-service-module-owners-bicep
12avm/res/api-management/service/authorization-server46d3xbcp.res.apimgmt-authzserveravm-res-apimanagement-service-module-owners-bicep
13avm/res/api-management/service/backend46d3xbcp.res.apimgmt-backendavm-res-apimanagement-service-module-owners-bicep
14avm/res/api-management/service/cache46d3xbcp.res.apimgmt-cacheavm-res-apimanagement-service-module-owners-bicep
15avm/res/api-management/service/diagnostics46d3xbcp.res.apimgmt-service-diagavm-res-apimanagement-service-module-owners-bicep
16avm/res/api-management/service/identity-provider46d3xbcp.res.apimgmt-identityprovideravm-res-apimanagement-service-module-owners-bicep
17avm/res/api-management/service/logger46d3xbcp.res.apimgmt-loggeravm-res-apimanagement-service-module-owners-bicep
18avm/res/api-management/service/named-value46d3xbcp.res.apimgmt-namedvalueavm-res-apimanagement-service-module-owners-bicep
19avm/res/api-management/service/policy46d3xbcp.res.apimgmt-policyavm-res-apimanagement-service-module-owners-bicep
20avm/res/api-management/service/portalsetting46d3xbcp.res.apimgmt-portalsettingavm-res-apimanagement-service-module-owners-bicep
21avm/res/api-management/service/private-endpoint-connection46d3xbcp.res.apimgmt-privendpointconnavm-res-apimanagement-service-module-owners-bicep
22avm/res/api-management/service/product46d3xbcp.res.apimgmt-productavm-res-apimanagement-service-module-owners-bicep
23avm/res/api-management/service/product/api46d3xbcp.res.apimgmt-productapiavm-res-apimanagement-service-module-owners-bicep
24avm/res/api-management/service/product/group46d3xbcp.res.apimgmt-productgroupavm-res-apimanagement-service-module-owners-bicep
25avm/res/api-management/service/product/policy46d3xbcp.res.apimgmt-service-productpolicyavm-res-apimanagement-service-module-owners-bicep
26avm/res/api-management/service/subscription46d3xbcp.res.apimgmt-subscriptionavm-res-apimanagement-service-module-owners-bicep
27avm/res/api-management/service/workspace46d3xbcp.res.apimgmt-workspaceavm-res-apimanagement-service-module-owners-bicep
28avm/res/api-management/service/workspace/api46d3xbcp.res.apimgmt-service-workspaceapiavm-res-apimanagement-service-module-owners-bicep
29avm/res/api-management/service/workspace/api-version-set46d3xbcp.res.apimgmt-service-workspaceapiversetavm-res-apimanagement-service-module-owners-bicep
30avm/res/api-management/service/workspace/api/diagnostics46d3xbcp.res.apimgmt-service-workspaceapidiagavm-res-apimanagement-service-module-owners-bicep
31avm/res/api-management/service/workspace/api/operation46d3xbcp.res.apimgmt-service-workspaceapiopavm-res-apimanagement-service-module-owners-bicep
32avm/res/api-management/service/workspace/api/operation/policy46d3xbcp.res.apimgmt-service-workspaceapioppolavm-res-apimanagement-service-module-owners-bicep
33avm/res/api-management/service/workspace/api/policy46d3xbcp.res.apimgmt-service-workspaceapipolavm-res-apimanagement-service-module-owners-bicep
34avm/res/api-management/service/workspace/backend46d3xbcp.res.apimgmt-service-workspacebackendavm-res-apimanagement-service-module-owners-bicep
35avm/res/api-management/service/workspace/diagnostics46d3xbcp.res.apimgmt-service-workspacediagavm-res-apimanagement-service-module-owners-bicep
36avm/res/api-management/service/workspace/logger46d3xbcp.res.apimgmt-service-workspaceloggeravm-res-apimanagement-service-module-owners-bicep
37avm/res/api-management/service/workspace/named-value46d3xbcp.res.apimgmt-service-workspacenamedvalavm-res-apimanagement-service-module-owners-bicep
38avm/res/api-management/service/workspace/policy46d3xbcp.res.apimgmt-service-workspacepolicyavm-res-apimanagement-service-module-owners-bicep
39avm/res/api-management/service/workspace/product46d3xbcp.res.apimgmt-service-workspaceproductavm-res-apimanagement-service-module-owners-bicep
40avm/res/api-management/service/workspace/product/api-link46d3xbcp.res.apimgmt-service-workspprodapilinkavm-res-apimanagement-service-module-owners-bicep
41avm/res/api-management/service/workspace/product/group-link46d3xbcp.res.apimgmt-service-workspprodgrplinkavm-res-apimanagement-service-module-owners-bicep
42avm/res/api-management/service/workspace/product/policy46d3xbcp.res.apimgmt-service-workspprodpolicyavm-res-apimanagement-service-module-owners-bicep
43avm/res/api-management/service/workspace/subscription46d3xbcp.res.apimgmt-service-workspacesubavm-res-apimanagement-service-module-owners-bicep
44avm/res/app-configuration/configuration-store46d3xbcp.res.appconfiguration-configurationstoreavm-res-appconfiguration-configurationstore-module-owners-bicep
45avm/res/app-configuration/configuration-store/key-value46d3xbcp.res.appconfig-configstore-keyvalueavm-res-appconfiguration-configurationstore-module-owners-bicep
46avm/res/app-configuration/configuration-store/replica46d3xbcp.res.appconfig-configstore-replicaavm-res-appconfiguration-configurationstore-module-owners-bicep
47avm/res/app/agent46d3xbcp.res.app-agentavm-res-app-agent-module-owners-bicep
48avm/res/app/container-app46d3xbcp.res.app-containerappavm-res-app-containerapp-module-owners-bicep
49avm/res/app/container-app/auth-config46d3xbcp.res.app-containerapp-authconfigavm-res-app-containerapp-module-owners-bicep
50avm/res/app/job46d3xbcp.res.app-jobavm-res-app-job-module-owners-bicep
51avm/res/app/managed-environment46d3xbcp.res.app-managedenvironmentavm-res-app-managedenvironment-module-owners-bicep
52avm/res/app/managed-environment/certificate46d3xbcp.res.app-managedenvironment-certificateavm-res-app-managedenvironment-module-owners-bicep
53avm/res/app/managed-environment/storage46d3xbcp.res.app-managedenvironment-storageavm-res-app-managedenvironment-module-owners-bicep
54avm/res/app/session-pool46d3xbcp.res.app-sessionpoolavm-res-app-sessionpool-module-owners-bicep
55avm/res/authorization/policy-assignment46d3xbcp.res.authz-policyassignmentavm-res-authorization-policyassignment-module-owners-bicep
56avm/res/authorization/policy-assignment/mg-scope46d3xbcp.res.authz-policyassignment_mgscopeavm-res-authorization-policyassignment-module-owners-bicep
57avm/res/authorization/policy-assignment/rg-scope46d3xbcp.res.authz-policyassignment_rgscopeavm-res-authorization-policyassignment-module-owners-bicep
58avm/res/authorization/policy-assignment/sub-scope46d3xbcp.res.authz-policyassignment_subscopeavm-res-authorization-policyassignment-module-owners-bicep
59avm/res/authorization/role-assignment46d3xbcp.res.authz-roleassignmentavm-res-authorization-roleassignment-module-owners-bicep
60avm/res/authorization/role-assignment/mg-scope46d3xbcp.res.authz-roleassignment_mgscopeavm-res-authorization-roleassignment-module-owners-bicep
61avm/res/authorization/role-assignment/rg-scope46d3xbcp.res.authz-roleassignment_rgscopeavm-res-authorization-roleassignment-module-owners-bicep
62avm/res/authorization/role-assignment/sub-scope46d3xbcp.res.authz-roleassignment_subscopeavm-res-authorization-roleassignment-module-owners-bicep
63avm/res/automation/automation-account46d3xbcp.res.automation-automationaccountavm-res-automation-automationaccount-module-owners-bicep
64avm/res/automation/automation-account/credential46d3xbcp.res.aut-autacct-credentialavm-res-automation-automationaccount-module-owners-bicep
65avm/res/automation/automation-account/hybrid-runbook-worker-group46d3xbcp.res.aut-autacct-hybrunbookwrkrgrpavm-res-automation-automationaccount-module-owners-bicep
66avm/res/automation/automation-account/hybrid-runbook-worker-group/hybrid-runbook-worker46d3xbcp.res.aut-autacct-hybrunbkwrkrgrphybruboavm-res-automation-automationaccount-module-owners-bicep
67avm/res/automation/automation-account/job-schedule46d3xbcp.res.aut-autacct-jobscheduleavm-res-automation-automationaccount-module-owners-bicep
68avm/res/automation/automation-account/module46d3xbcp.res.aut-autacct-moduleavm-res-automation-automationaccount-module-owners-bicep
69avm/res/automation/automation-account/powershell72-module46d3xbcp.res.aut-autacct-powersh72moduleavm-res-automation-automationaccount-module-owners-bicep
70avm/res/automation/automation-account/python2-package46d3xbcp.res.aut-autacct-python2packageavm-res-automation-automationaccount-module-owners-bicep
71avm/res/automation/automation-account/python3-package46d3xbcp.res.aut-autacct-python3packageavm-res-automation-automationaccount-module-owners-bicep
72avm/res/automation/automation-account/runbook46d3xbcp.res.aut-autacct-runbookavm-res-automation-automationaccount-module-owners-bicep
73avm/res/automation/automation-account/schedule46d3xbcp.res.aut-autacct-scheduleavm-res-automation-automationaccount-module-owners-bicep
74avm/res/automation/automation-account/source-control46d3xbcp.res.aut-autacct-sourcecontrolavm-res-automation-automationaccount-module-owners-bicep
75avm/res/automation/automation-account/variable46d3xbcp.res.aut-autacct-variableavm-res-automation-automationaccount-module-owners-bicep
76avm/res/automation/automation-account/webhook46d3xbcp.res.aut-autacct-webhookavm-res-automation-automationaccount-module-owners-bicep
77avm/res/avs/private-cloud46d3xbcp.res.avs-privatecloudavm-res-avs-privatecloud-module-owners-bicep
78avm/res/azure-stack-hci/cluster46d3xbcp.res.azurestackhci-clusteravm-res-azurestackhci-cluster-module-owners-bicep
79avm/res/azure-stack-hci/cluster/arc-setting/extension46d3xbcp.res.azurestackhci-clusterarcsettingavm-res-azurestackhci-cluster-module-owners-bicep
80avm/res/azure-stack-hci/cluster/deployment-setting46d3xbcp.res.azurestackhci-cluster-deplsettingavm-res-azurestackhci-cluster-module-owners-bicep
81avm/res/azure-stack-hci/logical-network46d3xbcp.res.azurestackhci-logicalnetworkavm-res-azurestackhci-logicalnetwork-module-owners-bicep
82avm/res/azure-stack-hci/marketplace-gallery-image46d3xbcp.res.azurestackhci-markplgalleryimgavm-res-azurestackhci-marketplacegalleryimage-module-owners-bicep
83avm/res/azure-stack-hci/network-interface46d3xbcp.res.azurestackhci-networkinterfaceavm-res-azurestackhci-networkinterface-module-owners-bicep
84avm/res/azure-stack-hci/virtual-hard-disk46d3xbcp.res.azurestackhci-virtualharddiskavm-res-azurestackhci-virtualharddisk-module-owners-bicep
85avm/res/azure-stack-hci/virtual-machine-instance46d3xbcp.res.azurestackhci-virtualmachineinstanceavm-res-azurestackhci-virtualmachineinstance-module-owners-bicep
86avm/res/batch/batch-account46d3xbcp.res.batch-batchaccountavm-res-batch-batchaccount-module-owners-bicep
87avm/res/bot-service/bot-service46d3xbcp.res.botservice-botserviceavm-res-botservice-botservice-module-owners-bicep
88avm/res/cache/redis46d3xbcp.res.cache-redisavm-res-cache-redis-module-owners-bicep
89avm/res/cache/redis-enterprise46d3xbcp.res.cache-redisenterpriseavm-res-cache-redisenterprise-module-owners-bicep
90avm/res/cache/redis-enterprise/database46d3xbcp.res.cache-redisenterprise-databaseavm-res-cache-redisenterprise-module-owners-bicep
91avm/res/cache/redis-enterprise/database/access-policy-assignment46d3xbcp.res.cache-redisenterprise-databaseaccessavm-res-cache-redisenterprise-module-owners-bicep
92avm/res/cache/redis/access-policy46d3xbcp.res.cache-redis-accesspolicyavm-res-cache-redis-module-owners-bicep
93avm/res/cache/redis/access-policy-assignment46d3xbcp.res.cache-redis-accesspolicyassignmentavm-res-cache-redis-module-owners-bicep
94avm/res/cache/redis/firewall-rule46d3xbcp.res.cache-redis-firewallruleavm-res-cache-redis-module-owners-bicep
95avm/res/cache/redis/linked-server46d3xbcp.res.cache-redis-linkedserveravm-res-cache-redis-module-owners-bicep
96avm/res/cdn/profile46d3xbcp.res.cdn-profileavm-res-cdn-profile-module-owners-bicep
97avm/res/cdn/profile/afd-endpoint46d3xbcp.res.cdn-profile-afdendpointavm-res-cdn-profile-module-owners-bicep
98avm/res/cdn/profile/afd-endpoint/route46d3xbcp.res.cdn-profile-afdendpointrouteavm-res-cdn-profile-module-owners-bicep
99avm/res/cdn/profile/custom-domain46d3xbcp.res.cdn-profile-domainavm-res-cdn-profile-module-owners-bicep
100avm/res/cdn/profile/endpoint46d3xbcp.res.cdn-profile-endpointavm-res-cdn-profile-module-owners-bicep
101avm/res/cdn/profile/endpoint/origin46d3xbcp.res.cdn-profile-endpointoriginavm-res-cdn-profile-module-owners-bicep
102avm/res/cdn/profile/origin-group46d3xbcp.res.cdn-profile-origingroupavm-res-cdn-profile-module-owners-bicep
103avm/res/cdn/profile/origin-group/origin46d3xbcp.res.cdn-profile-origingroup-originavm-res-cdn-profile-module-owners-bicep
104avm/res/cdn/profile/rule-set46d3xbcp.res.cdn-profile-rulesetavm-res-cdn-profile-module-owners-bicep
105avm/res/cdn/profile/rule-set/rule46d3xbcp.res.cdn-profile-rulesetruleavm-res-cdn-profile-module-owners-bicep
106avm/res/cdn/profile/secret46d3xbcp.res.cdn-profile-secretavm-res-cdn-profile-module-owners-bicep
107avm/res/cdn/profile/security-policy46d3xbcp.res.cdn-profile-securitypolicyavm-res-cdn-profile-module-owners-bicep
108avm/res/chaos/experiment46d3xbcp.res.chaos-experimentavm-res-chaos-experiment-module-owners-bicep
109avm/res/code-signing/code-signing-account46d3xbcp.res.codesigning-codesigningaccountavm-res-codesigning-codesigningaccount-module-owners-bicep
110avm/res/cognitive-services/account46d3xbcp.res.cognitiveservices-accountavm-res-cognitiveservices-account-module-owners-bicep
111avm/res/cognitive-services/account/project46d3xbcp.res.cognitiveservices-account-projectavm-res-cognitiveservices-account-module-owners-bicep
112avm/res/communication/communication-service46d3xbcp.res.communication-communicationserviceavm-res-communication-communicationservice-module-owners-bicep
113avm/res/communication/email-service46d3xbcp.res.communication-emailserviceavm-res-communication-emailservice-module-owners-bicep
114avm/res/communication/email-service/domain46d3xbcp.res.comm-emailservice-domainavm-res-communication-emailservice-module-owners-bicep
115avm/res/communication/email-service/domain/sender-username46d3xbcp.res.comm-emailservice-domsndrusrnameavm-res-communication-emailservice-module-owners-bicep
116avm/res/compute/availability-set46d3xbcp.res.compute-availabilitysetavm-res-compute-availabilityset-module-owners-bicep
117avm/res/compute/disk46d3xbcp.res.compute-diskavm-res-compute-disk-module-owners-bicep
118avm/res/compute/disk-encryption-set46d3xbcp.res.compute-diskencryptionsetavm-res-compute-diskencryptionset-module-owners-bicep
119avm/res/compute/gallery46d3xbcp.res.compute-galleryavm-res-compute-gallery-module-owners-bicep
120avm/res/compute/gallery/application46d3xbcp.res.compute-gallery-applicationavm-res-compute-gallery-module-owners-bicep
121avm/res/compute/gallery/image46d3xbcp.res.compute-gallery-imageavm-res-compute-gallery-module-owners-bicep
122avm/res/compute/image46d3xbcp.res.compute-imageavm-res-compute-image-module-owners-bicep
123avm/res/compute/proximity-placement-group46d3xbcp.res.compute-proximityplacementgroupavm-res-compute-proximityplacementgroup-module-owners-bicep
124avm/res/compute/ssh-public-key46d3xbcp.res.compute-sshpublickeyavm-res-compute-sshpublickey-module-owners-bicep
125avm/res/compute/virtual-machine46d3xbcp.res.compute-virtualmachineavm-res-compute-virtualmachine-module-owners-bicep
126avm/res/compute/virtual-machine-scale-set46d3xbcp.res.compute-virtualmachinescalesetavm-res-compute-virtualmachinescaleset-module-owners-bicep
127avm/res/compute/virtual-machine-scale-set/extension46d3xbcp.res.compute-vmss-extensionavm-res-compute-virtualmachinescaleset-module-owners-bicep
128avm/res/compute/virtual-machine/extension46d3xbcp.res.compute-vm-extensionavm-res-compute-virtualmachine-module-owners-bicep
129avm/res/consumption/budget46d3xbcp.res.consumption-budgetavm-res-consumption-budget-module-owners-bicep
130avm/res/consumption/budget/mg-scope46d3xbcp.res.consumption-budget_mgscopeavm-res-consumption-budget-module-owners-bicep
131avm/res/consumption/budget/rg-scope46d3xbcp.res.consumption-budget_rgscopeavm-res-consumption-budget-module-owners-bicep
132avm/res/consumption/budget/sub-scope46d3xbcp.res.consumption-budget_subscopeavm-res-consumption-budget-module-owners-bicep
133avm/res/container-instance/container-group46d3xbcp.res.containerinstance-containergroupavm-res-containerinstance-containergroup-module-owners-bicep
134avm/res/container-registry/registry46d3xbcp.res.containerregistry-registryavm-res-containerregistry-registry-module-owners-bicep
135avm/res/container-registry/registry/cache-rule46d3xbcp.res.containerregistry-registry-cacheruleavm-res-containerregistry-registry-module-owners-bicep
136avm/res/container-registry/registry/credential-set46d3xbcp.res.containerregistry-registry-credsetavm-res-containerregistry-registry-module-owners-bicep
137avm/res/container-registry/registry/replication46d3xbcp.res.containerregistry-registry-replavm-res-containerregistry-registry-module-owners-bicep
138avm/res/container-registry/registry/scope-map46d3xbcp.res.containerregistry-registry-scopemapavm-res-containerregistry-registry-module-owners-bicep
139avm/res/container-registry/registry/task46d3xbcp.res.containerregistry-registry-taskavm-res-containerregistry-registry-module-owners-bicep
140avm/res/container-registry/registry/token46d3xbcp.res.containerregistry-registry-tokenavm-res-containerregistry-registry-module-owners-bicep
141avm/res/container-registry/registry/webhook46d3xbcp.res.containerregistry-registry-webhookavm-res-containerregistry-registry-module-owners-bicep
142avm/res/container-service/managed-cluster46d3xbcp.res.containerservice-managedclusteravm-res-containerservice-managedcluster-module-owners-bicep
143avm/res/container-service/managed-cluster/agent-pool46d3xbcp.res.consvc-mgdcluster-agentpoolavm-res-containerservice-managedcluster-module-owners-bicep
144avm/res/container-service/managed-cluster/maintenance-configuration46d3xbcp.res.consvc-mgdcluster-maintenancecfgavm-res-containerservice-managedcluster-module-owners-bicep
145avm/res/dashboard/grafana46d3xbcp.res.dashboard-grafanaavm-res-dashboard-grafana-module-owners-bicep
146avm/res/data-factory/factory46d3xbcp.res.datafactory-factoryavm-res-datafactory-factory-module-owners-bicep
147avm/res/data-factory/factory/integration-runtime46d3xbcp.res.datafactory-factory-integrruntimeavm-res-datafactory-factory-module-owners-bicep
148avm/res/data-factory/factory/linked-service46d3xbcp.res.datafactory-factory-linkedserviceavm-res-datafactory-factory-module-owners-bicep
149avm/res/data-factory/factory/managed-virtual-network46d3xbcp.res.datafactory-factory-managedvnetavm-res-datafactory-factory-module-owners-bicep
150avm/res/data-factory/factory/managed-virtual-network/managed-private-endpoint46d3xbcp.res.datafactory-factory-managedvnetmgdpeavm-res-datafactory-factory-module-owners-bicep
151avm/res/data-protection/backup-vault46d3xbcp.res.dataprotection-backupvaultavm-res-dataprotection-backupvault-module-owners-bicep
152avm/res/data-protection/backup-vault/backup-instance46d3xbcp.res.dataprot-backupvault-backupinstavm-res-dataprotection-backupvault-module-owners-bicep
153avm/res/data-protection/backup-vault/backup-policy46d3xbcp.res.dataprot-backupvault-backuppolicyavm-res-dataprotection-backupvault-module-owners-bicep
154avm/res/data-protection/resource-guard46d3xbcp.res.dataprotection-resourceguardavm-res-dataprotection-resourceguard-module-owners-bicep
155avm/res/databricks/access-connector46d3xbcp.res.databricks-accessconnectoravm-res-databricks-accessconnector-module-owners-bicep
156avm/res/databricks/workspace46d3xbcp.res.databricks-workspaceavm-res-databricks-workspace-module-owners-bicep
157avm/res/db-for-my-sql/flexible-server46d3xbcp.res.dbformysql-flexibleserveravm-res-dbformysql-flexibleserver-module-owners-bicep
158avm/res/db-for-my-sql/flexible-server/administrator46d3xbcp.res.dbformysql-flexisrv-administratoravm-res-dbformysql-flexibleserver-module-owners-bicep
159avm/res/db-for-my-sql/flexible-server/advanced-threat-protection46d3xbcp.res.dbformysql-flexisrv-advdthreatprotavm-res-dbformysql-flexibleserver-module-owners-bicep
160avm/res/db-for-my-sql/flexible-server/configuration46d3xbcp.res.dbformysql-flexisrv-configurationavm-res-dbformysql-flexibleserver-module-owners-bicep
161avm/res/db-for-my-sql/flexible-server/database46d3xbcp.res.dbformysql-flexisrv-databaseavm-res-dbformysql-flexibleserver-module-owners-bicep
162avm/res/db-for-my-sql/flexible-server/firewall-rule46d3xbcp.res.dbformysql-flexisrv-firewallruleavm-res-dbformysql-flexibleserver-module-owners-bicep
163avm/res/db-for-postgre-sql/flexible-server46d3xbcp.res.dbforpostgresql-flexibleserveravm-res-dbforpostgresql-flexibleserver-module-owners-bicep
164avm/res/db-for-postgre-sql/flexible-server/administrator46d3xbcp.res.dbforpostgresql-flexisrv-adminavm-res-dbforpostgresql-flexibleserver-module-owners-bicep
165avm/res/db-for-postgre-sql/flexible-server/advanced-threat-protection-setting46d3xbcp.res.dbforpostgresql-flexisrv-adthprotstgavm-res-dbforpostgresql-flexibleserver-module-owners-bicep
166avm/res/db-for-postgre-sql/flexible-server/configuration46d3xbcp.res.dbforpostgresql-flexisrv-configavm-res-dbforpostgresql-flexibleserver-module-owners-bicep
167avm/res/db-for-postgre-sql/flexible-server/database46d3xbcp.res.dbforpostgresql-flexisrv-databaseavm-res-dbforpostgresql-flexibleserver-module-owners-bicep
168avm/res/db-for-postgre-sql/flexible-server/firewall-rule46d3xbcp.res.dbforpostgresql-flexisrv-fwruleavm-res-dbforpostgresql-flexibleserver-module-owners-bicep
169avm/res/desktop-virtualization/application-group46d3xbcp.res.desktopvirtualization-appgroupavm-res-desktopvirtualization-applicationgroup-module-owners-bicep
170avm/res/desktop-virtualization/application-group/application46d3xbcp.res.desktopvirtualization-appgroup-appavm-res-desktopvirtualization-applicationgroup-module-owners-bicep
171avm/res/desktop-virtualization/host-pool46d3xbcp.res.desktopvirtualization-hostpoolavm-res-desktopvirtualization-hostpool-module-owners-bicep
172avm/res/desktop-virtualization/scaling-plan46d3xbcp.res.desktopvirtualization-scalingplanavm-res-desktopvirtualization-scalingplan-module-owners-bicep
173avm/res/desktop-virtualization/workspace46d3xbcp.res.desktopvirtualization-workspaceavm-res-desktopvirtualization-workspace-module-owners-bicep
174avm/res/dev-center/devcenter46d3xbcp.res.devcenter-devcenteravm-res-devcenter-devcenter-module-owners-bicep
175avm/res/dev-center/devcenter/attachednetwork46d3xbcp.res.devcenter-devcenter-attachednetworkavm-res-devcenter-devcenter-module-owners-bicep
176avm/res/dev-center/devcenter/catalog46d3xbcp.res.devcenter-devcenter-catalogavm-res-devcenter-devcenter-module-owners-bicep
177avm/res/dev-center/devcenter/devboxdefinition46d3xbcp.res.devcenter-devcenter-devboxdefinitionavm-res-devcenter-devcenter-module-owners-bicep
178avm/res/dev-center/devcenter/environment-type46d3xbcp.res.devcenter-devcenter-environmenttypeavm-res-devcenter-devcenter-module-owners-bicep
179avm/res/dev-center/devcenter/gallery46d3xbcp.res.devcenter-devcenter-galleryavm-res-devcenter-devcenter-module-owners-bicep
180avm/res/dev-center/devcenter/project-policy46d3xbcp.res.devcenter-devcenter-projectpolicyavm-res-devcenter-devcenter-module-owners-bicep
181avm/res/dev-center/network-connection46d3xbcp.res.devcenter-networkconnectionavm-res-devcenter-networkconnection-module-owners-bicep
182avm/res/dev-center/project46d3xbcp.res.devcenter-projectavm-res-devcenter-project-module-owners-bicep
183avm/res/dev-center/project/catalog46d3xbcp.res.devcenter-project-catalogavm-res-devcenter-project-module-owners-bicep
184avm/res/dev-center/project/environment-type46d3xbcp.res.devcenter-project-environmenttypeavm-res-devcenter-project-module-owners-bicep
185avm/res/dev-center/project/pool46d3xbcp.res.devcenter-project-poolavm-res-devcenter-project-module-owners-bicep
186avm/res/dev-center/project/pool/schedule46d3xbcp.res.devcenter-project-poolscheduleavm-res-devcenter-project-module-owners-bicep
187avm/res/dev-ops-infrastructure/pool46d3xbcp.res.devopsinfrastructure-poolavm-res-devopsinfrastructure-pool-module-owners-bicep
188avm/res/dev-test-lab/lab46d3xbcp.res.devtestlab-labavm-res-devtestlab-lab-module-owners-bicep
189avm/res/dev-test-lab/lab/artifactsource46d3xbcp.res.devtestlab-lab-artifactsourceavm-res-devtestlab-lab-module-owners-bicep
190avm/res/dev-test-lab/lab/cost46d3xbcp.res.devtestlab-lab-costavm-res-devtestlab-lab-module-owners-bicep
191avm/res/dev-test-lab/lab/notificationchannel46d3xbcp.res.devtestlab-lab-notificationchannelavm-res-devtestlab-lab-module-owners-bicep
192avm/res/dev-test-lab/lab/policyset/policy46d3xbcp.res.devtestlab-lab-policysetpolicyavm-res-devtestlab-lab-module-owners-bicep
193avm/res/dev-test-lab/lab/schedule46d3xbcp.res.devtestlab-lab-scheduleavm-res-devtestlab-lab-module-owners-bicep
194avm/res/dev-test-lab/lab/secret46d3xbcp.res.devtestlab-lab-secretavm-res-devtestlab-lab-module-owners-bicep
195avm/res/dev-test-lab/lab/virtualnetwork46d3xbcp.res.devtestlab-lab-virtualnetworkavm-res-devtestlab-lab-module-owners-bicep
196avm/res/devices/iot-hub46d3xbcp.res.devices-iothubavm-res-devices-iothub-module-owners-bicep
197avm/res/digital-twins/digital-twins-instance46d3xbcp.res.digitaltwins-digitaltwinsinstanceavm-res-digitaltwins-digitaltwinsinstance-module-owners-bicep
198avm/res/digital-twins/digital-twins-instance/endpoint46d3xbcp.res.digitaltwins-digitaltwinsinstance-enavm-res-digitaltwins-digitaltwinsinstance-module-owners-bicep
199avm/res/document-db/database-account46d3xbcp.res.documentdb-databaseaccountavm-res-documentdb-databaseaccount-module-owners-bicep
200avm/res/document-db/database-account/cassandra-keyspace46d3xbcp.res.doctdb-dbacct-cassandrkeyspaceavm-res-documentdb-databaseaccount-module-owners-bicep
201avm/res/document-db/database-account/cassandra-keyspace/table46d3xbcp.res.doctdb-dbacct-cassandrkeyspacetableavm-res-documentdb-databaseaccount-module-owners-bicep
202avm/res/document-db/database-account/cassandra-keyspace/view46d3xbcp.res.doctdb-dbacct-cassandrkeyspaceviewavm-res-documentdb-databaseaccount-module-owners-bicep
203avm/res/document-db/database-account/cassandra-role-assignment46d3xbcp.res.doctdb-dbacct-cassandrroleassignmentavm-res-documentdb-databaseaccount-module-owners-bicep
204avm/res/document-db/database-account/cassandra-role-definition46d3xbcp.res.doctdb-dbacct-cassandrroledefinitionavm-res-documentdb-databaseaccount-module-owners-bicep
205avm/res/document-db/database-account/gremlin-database46d3xbcp.res.doctdb-dbacct-gremlindbavm-res-documentdb-databaseaccount-module-owners-bicep
206avm/res/document-db/database-account/gremlin-database/graph46d3xbcp.res.doctdb-dbacct-gremlindbgraphavm-res-documentdb-databaseaccount-module-owners-bicep
207avm/res/document-db/database-account/mongodb-database46d3xbcp.res.doctdb-dbacct-mongodbdatabaseavm-res-documentdb-databaseaccount-module-owners-bicep
208avm/res/document-db/database-account/mongodb-database/collection46d3xbcp.res.doctdb-dbacct-mongodbdbcollectionavm-res-documentdb-databaseaccount-module-owners-bicep
209avm/res/document-db/database-account/sql-database46d3xbcp.res.documentdb-databaseaccountsqldbavm-res-documentdb-databaseaccount-module-owners-bicep
210avm/res/document-db/database-account/sql-database/container46d3xbcp.res.doctdb-dbacct-sqldbcontaineravm-res-documentdb-databaseaccount-module-owners-bicep
211avm/res/document-db/database-account/sql-role-assignment46d3xbcp.res.doctdb-dbacct-sqlroleassignmentavm-res-documentdb-databaseaccount-module-owners-bicep
212avm/res/document-db/database-account/sql-role-definition46d3xbcp.res.doctdb-dbacct-sqlroledefinitionavm-res-documentdb-databaseaccount-module-owners-bicep
213avm/res/document-db/database-account/table46d3xbcp.res.documentdb-databaseaccounttableavm-res-documentdb-databaseaccount-module-owners-bicep
214avm/res/document-db/mongo-cluster46d3xbcp.res.documentdb-mongoclusteravm-res-documentdb-mongocluster-module-owners-bicep
215avm/res/document-db/mongo-cluster/firewall-rule46d3xbcp.res.documentdb-mongocluster-firewallruleavm-res-documentdb-mongocluster-module-owners-bicep
216avm/res/document-db/mongo-cluster/user46d3xbcp.res.documentdb-mongocluster-useravm-res-documentdb-mongocluster-module-owners-bicep
217avm/res/durable-task/scheduler46d3xbcp.res.durabletask-scheduleravm-res-durabletask-scheduler-module-owners-bicep
218avm/res/edge-order/order-item46d3xbcp.res.edgeorder-orderitemavm-res-edgeorder-orderitem-module-owners-bicep
219avm/res/edge/configuration46d3xbcp.res.edge-configurationavm-res-edge-configuration-module-owners-bicep
220avm/res/edge/site46d3xbcp.res.edge-siteavm-res-edge-site-module-owners-bicep
221avm/res/edge/site/rg-scope46d3xbcp.res.edge-site_rgscopeavm-res-edge-site-module-owners-bicep
222avm/res/edge/site/sub-scope46d3xbcp.res.edge-site_subscopeavm-res-edge-site-module-owners-bicep
223avm/res/elastic-san/elastic-san46d3xbcp.res.elasticsan-elasticsanavm-res-elasticsan-elasticsan-module-owners-bicep
224avm/res/elastic-san/elastic-san/volume-group46d3xbcp.res.elsan-elsan-volumegroupavm-res-elasticsan-elasticsan-module-owners-bicep
225avm/res/elastic-san/elastic-san/volume-group/snapshot46d3xbcp.res.elsan-elsan-volumegroupsnapshotavm-res-elasticsan-elasticsan-module-owners-bicep
226avm/res/elastic-san/elastic-san/volume-group/volume46d3xbcp.res.elsan-elsan-volumegroupvolumeavm-res-elasticsan-elasticsan-module-owners-bicep
227avm/res/event-grid/domain46d3xbcp.res.eventgrid-domainavm-res-eventgrid-domain-module-owners-bicep
228avm/res/event-grid/domain/event-subscription46d3xbcp.res.eventgrid-domain-eventsubscriptionavm-res-eventgrid-domain-module-owners-bicep
229avm/res/event-grid/domain/topic46d3xbcp.res.eventgrid-domain-topicavm-res-eventgrid-domain-module-owners-bicep
230avm/res/event-grid/namespace46d3xbcp.res.eventgrid-namespaceavm-res-eventgrid-namespace-module-owners-bicep
231avm/res/event-grid/namespace/ca-certificate46d3xbcp.res.eventgrid-namespace-cacertificateavm-res-eventgrid-namespace-module-owners-bicep
232avm/res/event-grid/namespace/client46d3xbcp.res.eventgrid-namespace-clientavm-res-eventgrid-namespace-module-owners-bicep
233avm/res/event-grid/namespace/client-group46d3xbcp.res.eventgrid-namespace-clientgroupavm-res-eventgrid-namespace-module-owners-bicep
234avm/res/event-grid/namespace/permission-binding46d3xbcp.res.eventgrid-namespace-permbindingavm-res-eventgrid-namespace-module-owners-bicep
235avm/res/event-grid/namespace/topic46d3xbcp.res.eventgrid-namespace-topicavm-res-eventgrid-namespace-module-owners-bicep
236avm/res/event-grid/namespace/topic-space46d3xbcp.res.eventgrid-namespace-topicspaceavm-res-eventgrid-namespace-module-owners-bicep
237avm/res/event-grid/namespace/topic/event-subscription46d3xbcp.res.eventgrid-namespace-topiceventsubavm-res-eventgrid-namespace-module-owners-bicep
238avm/res/event-grid/system-topic46d3xbcp.res.eventgrid-systemtopicavm-res-eventgrid-systemtopic-module-owners-bicep
239avm/res/event-grid/system-topic/event-subscription46d3xbcp.res.eventgrid-systemtopic-eventsubavm-res-eventgrid-systemtopic-module-owners-bicep
240avm/res/event-grid/topic46d3xbcp.res.eventgrid-topicavm-res-eventgrid-topic-module-owners-bicep
241avm/res/event-grid/topic/event-subscription46d3xbcp.res.eventgrid-topic-eventsubavm-res-eventgrid-topic-module-owners-bicep
242avm/res/event-hub/namespace46d3xbcp.res.eventhub-namespaceavm-res-eventhub-namespace-module-owners-bicep
243avm/res/event-hub/namespace/authorization-rule46d3xbcp.res.eventhub-namespace-authorizationruleavm-res-eventhub-namespace-module-owners-bicep
244avm/res/event-hub/namespace/disaster-recovery-config46d3xbcp.res.eventhub-namespace-drconfigavm-res-eventhub-namespace-module-owners-bicep
245avm/res/event-hub/namespace/eventhub46d3xbcp.res.eventhub-nseventhubavm-res-eventhub-namespace-module-owners-bicep
246avm/res/event-hub/namespace/eventhub/authorization-rule46d3xbcp.res.eventhub-namespace-eventhubauthruleavm-res-eventhub-namespace-module-owners-bicep
247avm/res/event-hub/namespace/eventhub/consumergroup46d3xbcp.res.eventhub-namespace-eventhubconsgrpavm-res-eventhub-namespace-module-owners-bicep
248avm/res/event-hub/namespace/network-rule-set46d3xbcp.res.eventhub-namespace-networkrulesetavm-res-eventhub-namespace-module-owners-bicep
249avm/res/fabric/capacity46d3xbcp.res.fabric-capacityavm-res-fabric-capacity-module-owners-bicep
250avm/res/health-bot/health-bot46d3xbcp.res.healthbot-healthbotavm-res-healthbot-healthbot-module-owners-bicep
251avm/res/healthcare-apis/workspace46d3xbcp.res.healthcareapis-workspaceavm-res-healthcareapis-workspace-module-owners-bicep
252avm/res/healthcare-apis/workspace/dicomservice46d3xbcp.res.healthcapis-workspace-dicomservicavm-res-healthcareapis-workspace-module-owners-bicep
253avm/res/healthcare-apis/workspace/fhirservice46d3xbcp.res.healthcapis-workspace-fhirserviceavm-res-healthcareapis-workspace-module-owners-bicep
254avm/res/healthcare-apis/workspace/iotconnector46d3xbcp.res.healthcapis-workspace-iotconnavm-res-healthcareapis-workspace-module-owners-bicep
255avm/res/healthcare-apis/workspace/iotconnector/fhirdestination46d3xbcp.res.healthcapis-workspace-iotconnfhirdstavm-res-healthcareapis-workspace-module-owners-bicep
256avm/res/hybrid-compute/gateway46d3xbcp.res.hybridcompute-gatewayavm-res-hybridcompute-gateway-module-owners-bicep
257avm/res/hybrid-compute/license46d3xbcp.res.hybridcompute-licenseavm-res-hybridcompute-license-module-owners-bicep
258avm/res/hybrid-compute/machine46d3xbcp.res.hybridcompute-machineavm-res-hybridcompute-machine-module-owners-bicep
259avm/res/hybrid-compute/machine/extension46d3xbcp.res.hybridcompute-machine-extensionavm-res-hybridcompute-machine-module-owners-bicep
260avm/res/hybrid-compute/private-link-scope46d3xbcp.res.hybridcompute-privatelinkscopeavm-res-hybridcompute-privatelinkscope-module-owners-bicep
261avm/res/hybrid-compute/setting46d3xbcp.res.hybridcompute-settingavm-res-hybridcompute-setting-module-owners-bicep
262avm/res/hybrid-container-service/provisioned-cluster-instance46d3xbcp.res.hybcontsvc-provclustinstavm-res-hybridcontainerservice-provisionedclusterinstance-module-owners-bicep
263avm/res/insights/action-group46d3xbcp.res.insights-actiongroupavm-res-insights-actiongroup-module-owners-bicep
264avm/res/insights/activity-log-alert46d3xbcp.res.insights-activitylogalertavm-res-insights-activitylogalert-module-owners-bicep
265avm/res/insights/autoscale-setting46d3xbcp.res.insights-autoscalesettingavm-res-insights-autoscalesetting-module-owners-bicep
266avm/res/insights/component46d3xbcp.res.insights-componentavm-res-insights-component-module-owners-bicep
267avm/res/insights/component/linked-storage-account46d3xbcp.res.insights-component-linkedstoracctavm-res-insights-component-module-owners-bicep
268avm/res/insights/data-collection-endpoint46d3xbcp.res.insights-datacollectionendpointavm-res-insights-datacollectionendpoint-module-owners-bicep
269avm/res/insights/data-collection-rule46d3xbcp.res.insights-datacollectionruleavm-res-insights-datacollectionrule-module-owners-bicep
270avm/res/insights/diagnostic-setting46d3xbcp.res.insights-diagnosticsettingavm-res-insights-diagnosticsetting-module-owners-bicep
271avm/res/insights/metric-alert46d3xbcp.res.insights-metricalertavm-res-insights-metricalert-module-owners-bicep
272avm/res/insights/private-link-scope46d3xbcp.res.insights-privatelinkscopeavm-res-insights-privatelinkscope-module-owners-bicep
273avm/res/insights/private-link-scope/scoped-resource46d3xbcp.res.insights-privatelinkscope-scopedresavm-res-insights-privatelinkscope-module-owners-bicep
274avm/res/insights/scheduled-query-rule46d3xbcp.res.insights-scheduledqueryruleavm-res-insights-scheduledqueryrule-module-owners-bicep
275avm/res/insights/webtest46d3xbcp.res.insights-webtestavm-res-insights-webtest-module-owners-bicep
276avm/res/iot-operations/instance46d3xbcp.res.iotoperations-instanceavm-res-iotoperations-instance-module-owners-bicep
277avm/res/key-vault/managed-hsm46d3xbcp.res.keyvault-managedhsmavm-res-keyvault-managedhsm-module-owners-bicep
278avm/res/key-vault/vault46d3xbcp.res.keyvault-vaultavm-res-keyvault-vault-module-owners-bicep
279avm/res/key-vault/vault/access-policy46d3xbcp.res.keyvault-accesspolicyavm-res-keyvault-vault-module-owners-bicep
280avm/res/key-vault/vault/key46d3xbcp.res.keyvault-keyavm-res-keyvault-vault-module-owners-bicep
281avm/res/key-vault/vault/secret46d3xbcp.res.keyvault-secretavm-res-keyvault-vault-module-owners-bicep
282avm/res/kubernetes-configuration/extension46d3xbcp.res.kubernetesconfiguration-extensionavm-res-kubernetesconfiguration-extension-module-owners-bicep
283avm/res/kubernetes-configuration/flux-configuration46d3xbcp.res.kubernetesconfiguration-fluxconfigavm-res-kubernetesconfiguration-fluxconfiguration-module-owners-bicep
284avm/res/kubernetes-runtime/bpg-peer46d3xbcp.res.kubernetesruntime-bgppeeravm-res-kubernetesruntime-bpgpeer-module-owners-bicep
285avm/res/kubernetes-runtime/load-balancer46d3xbcp.res.kubernetesruntime-loadbalanceravm-res-kubernetesruntime-loadbalancer-module-owners-bicep
286avm/res/kubernetes-runtime/service46d3xbcp.res.kubernetesruntime-serviceavm-res-kubernetesruntime-service-module-owners-bicep
287avm/res/kubernetes/connected-cluster46d3xbcp.res.kubernetes-connectedclusteravm-res-kubernetes-connectedcluster-module-owners-bicep
288avm/res/kusto/cluster46d3xbcp.res.kusto-clusteravm-res-kusto-cluster-module-owners-bicep
289avm/res/kusto/cluster/database46d3xbcp.res.kusto-cluster-databaseavm-res-kusto-cluster-module-owners-bicep
290avm/res/kusto/cluster/database/principal-assignment46d3xbcp.res.kusto-cluster-dbprinassignmentavm-res-kusto-cluster-module-owners-bicep
291avm/res/kusto/cluster/principal-assignment46d3xbcp.res.kusto-cluster-principalassignmentavm-res-kusto-cluster-module-owners-bicep
292avm/res/load-test-service/load-test46d3xbcp.res.loadtestservice-loadtestavm-res-loadtestservice-loadtest-module-owners-bicep
293avm/res/logic/integration-account46d3xbcp.res.logic-integrationaccountavm-res-logic-integrationaccount-module-owners-bicep
294avm/res/logic/integration-account/agreement46d3xbcp.res.logic-integrationaccount-agreementavm-res-logic-integrationaccount-module-owners-bicep
295avm/res/logic/integration-account/assembly46d3xbcp.res.logic-integrationaccount-assemblyavm-res-logic-integrationaccount-module-owners-bicep
296avm/res/logic/integration-account/certificate46d3xbcp.res.logic-integrationaccount-certificateavm-res-logic-integrationaccount-module-owners-bicep
297avm/res/logic/integration-account/map46d3xbcp.res.logic-integrationaccount-mapavm-res-logic-integrationaccount-module-owners-bicep
298avm/res/logic/integration-account/partner46d3xbcp.res.logic-integrationaccount-partneravm-res-logic-integrationaccount-module-owners-bicep
299avm/res/logic/integration-account/schema46d3xbcp.res.logic-integrationaccount-schemaavm-res-logic-integrationaccount-module-owners-bicep
300avm/res/logic/workflow46d3xbcp.res.logic-workflowavm-res-logic-workflow-module-owners-bicep
301avm/res/machine-learning-services/registry46d3xbcp.res.machinelearningservices-registryavm-res-machinelearningservices-registry-module-owners-bicep
302avm/res/machine-learning-services/workspace46d3xbcp.res.machinelearningservices-workspaceavm-res-machinelearningservices-workspace-module-owners-bicep
303avm/res/machine-learning-services/workspace/compute46d3xbcp.res.mlservices-workspace-computeavm-res-machinelearningservices-workspace-module-owners-bicep
304avm/res/machine-learning-services/workspace/connection46d3xbcp.res.mlservices-workspace-connectionavm-res-machinelearningservices-workspace-module-owners-bicep
305avm/res/machine-learning-services/workspace/datastore46d3xbcp.res.mlservices-workspace-datastoreavm-res-machinelearningservices-workspace-module-owners-bicep
306avm/res/maintenance/configuration-assignment46d3xbcp.res.maintenance-configurationassignmentavm-res-maintenance-configurationassignment-module-owners-bicep
307avm/res/maintenance/maintenance-configuration46d3xbcp.res.maintenance-maintenanceconfigurationavm-res-maintenance-maintenanceconfiguration-module-owners-bicep
308avm/res/managed-identity/user-assigned-identity46d3xbcp.res.managedidentity-userassignedidentityavm-res-managedidentity-userassignedidentity-module-owners-bicep
309avm/res/managed-identity/user-assigned-identity/federated-identity-credential46d3xbcp.res.managedid-userassignedidcredentialavm-res-managedidentity-userassignedidentity-module-owners-bicep
310avm/res/managed-services/registration-definition46d3xbcp.res.managedservices-registrationdefavm-res-managedservices-registrationdefinition-module-owners-bicep
311avm/res/management/management-group46d3xbcp.res.management-managementgroupavm-res-management-managementgroup-module-owners-bicep
312avm/res/management/service-group46d3xbcp.res.management-servicegroupavm-res-management-servicegroup-module-owners-bicep
313avm/res/maps/account46d3xbcp.res.maps-accountavm-res-maps-account-module-owners-bicep
314avm/res/net-app/net-app-account46d3xbcp.res.netapp-netappaccountavm-res-netapp-netappaccount-module-owners-bicep
315avm/res/net-app/net-app-account/backup-policy46d3xbcp.res.netapp-netappaccount-backuppolicyavm-res-netapp-netappaccount-module-owners-bicep
316avm/res/net-app/net-app-account/backup-vault46d3xbcp.res.netapp-netappaccount-backupvaultavm-res-netapp-netappaccount-module-owners-bicep
317avm/res/net-app/net-app-account/backup-vault/backup46d3xbcp.res.netapp-netappaccount-backupvaultbckpavm-res-netapp-netappaccount-module-owners-bicep
318avm/res/net-app/net-app-account/capacity-pool46d3xbcp.res.netapp-netappaccount-capacitypoolavm-res-netapp-netappaccount-module-owners-bicep
319avm/res/net-app/net-app-account/capacity-pool/volume46d3xbcp.res.netapp-netappaccount-capacitypoolvolavm-res-netapp-netappaccount-module-owners-bicep
320avm/res/net-app/net-app-account/snapshot-policy46d3xbcp.res.netapp-netappaccount-snapshotpolicyavm-res-netapp-netappaccount-module-owners-bicep
321avm/res/network/application-gateway46d3xbcp.res.network-appgwavm-res-network-applicationgateway-module-owners-bicep
322avm/res/network/application-gateway-web-application-firewall-policy46d3xbcp.res.network-appgwwebappfirewallpolicyavm-res-network-applicationgatewaywebapplicationfirewallpolicy-module-owners-bicep
323avm/res/network/application-security-group46d3xbcp.res.network-applicationsecuritygroupavm-res-network-applicationsecuritygroup-module-owners-bicep
324avm/res/network/azure-firewall46d3xbcp.res.network-azurefirewallavm-res-network-azurefirewall-module-owners-bicep
325avm/res/network/bastion-host46d3xbcp.res.network-bastionhostavm-res-network-bastionhost-module-owners-bicep
326avm/res/network/connection46d3xbcp.res.network-connectionavm-res-network-connection-module-owners-bicep
327avm/res/network/ddos-protection-plan46d3xbcp.res.network-ddosprotectionplanavm-res-network-ddosprotectionplan-module-owners-bicep
328avm/res/network/dns-forwarding-ruleset46d3xbcp.res.network-dnsforwardingrulesetavm-res-network-dnsforwardingruleset-module-owners-bicep
329avm/res/network/dns-forwarding-ruleset/forwarding-rule46d3xbcp.res.network-dnsforwardingruleset-fwdruleavm-res-network-dnsforwardingruleset-module-owners-bicep
330avm/res/network/dns-forwarding-ruleset/virtual-network-link46d3xbcp.res.network-dnsfwdruleset-vnetlinkavm-res-network-dnsforwardingruleset-module-owners-bicep
331avm/res/network/dns-resolver46d3xbcp.res.network-dnsresolveravm-res-network-dnsresolver-module-owners-bicep
332avm/res/network/dns-resolver/inbound-endpoint46d3xbcp.res.network-dnsresolver-inboundendpointavm-res-network-dnsresolver-module-owners-bicep
333avm/res/network/dns-resolver/outbound-endpoint46d3xbcp.res.network-dnsresolver-outboundendpointavm-res-network-dnsresolver-module-owners-bicep
334avm/res/network/dns-zone46d3xbcp.res.network-dnszoneavm-res-network-dnszone-module-owners-bicep
335avm/res/network/dns-zone/a46d3xbcp.res.network-dnszone-aavm-res-network-dnszone-module-owners-bicep
336avm/res/network/dns-zone/aaaa46d3xbcp.res.network-dnszone-aaaaavm-res-network-dnszone-module-owners-bicep
337avm/res/network/dns-zone/caa46d3xbcp.res.network-dnszone-caaavm-res-network-dnszone-module-owners-bicep
338avm/res/network/dns-zone/cname46d3xbcp.res.network-dnszone-cnameavm-res-network-dnszone-module-owners-bicep
339avm/res/network/dns-zone/dnssec-config46d3xbcp.res.network-dnszone-dnssecconfigavm-res-network-dnszone-module-owners-bicep
340avm/res/network/dns-zone/mx46d3xbcp.res.network-dnszone-mxavm-res-network-dnszone-module-owners-bicep
341avm/res/network/dns-zone/ns46d3xbcp.res.network-dnszone-nsavm-res-network-dnszone-module-owners-bicep
342avm/res/network/dns-zone/ptr46d3xbcp.res.network-dnszone-ptravm-res-network-dnszone-module-owners-bicep
343avm/res/network/dns-zone/soa46d3xbcp.res.network-dnszone-soaavm-res-network-dnszone-module-owners-bicep
344avm/res/network/dns-zone/srv46d3xbcp.res.network-dnszone-srvavm-res-network-dnszone-module-owners-bicep
345avm/res/network/dns-zone/txt46d3xbcp.res.network-dnszone-txtavm-res-network-dnszone-module-owners-bicep
346avm/res/network/express-route-circuit46d3xbcp.res.network-expressroutecircuitavm-res-network-expressroutecircuit-module-owners-bicep
347avm/res/network/express-route-gateway46d3xbcp.res.network-expressroutegatewayavm-res-network-expressroutegateway-module-owners-bicep
348avm/res/network/express-route-port46d3xbcp.res.network-expressrouteportavm-res-network-expressrouteport-module-owners-bicep
349avm/res/network/firewall-policy46d3xbcp.res.network-firewallpolicyavm-res-network-firewallpolicy-module-owners-bicep
350avm/res/network/firewall-policy/rule-collection-group46d3xbcp.res.network-fwpolicy-rulecollectionavm-res-network-firewallpolicy-module-owners-bicep
351avm/res/network/front-door46d3xbcp.res.network-frontdooravm-res-network-frontdoor-module-owners-bicep
352avm/res/network/front-door-web-application-firewall-policy46d3xbcp.res.network-frontdoorwebappfwpolicyavm-res-network-frontdoorwebapplicationfirewallpolicy-module-owners-bicep
353avm/res/network/ip-group46d3xbcp.res.network-ipgroupavm-res-network-ipgroup-module-owners-bicep
354avm/res/network/load-balancer46d3xbcp.res.network-loadbalanceravm-res-network-loadbalancer-module-owners-bicep
355avm/res/network/load-balancer/backend-address-pool46d3xbcp.res.network-loadbalancer-beaddresspoolavm-res-network-loadbalancer-module-owners-bicep
356avm/res/network/load-balancer/inbound-nat-rule46d3xbcp.res.network-loadbalancer-inboundnatruleavm-res-network-loadbalancer-module-owners-bicep
357avm/res/network/local-network-gateway46d3xbcp.res.network-localnetworkgatewayavm-res-network-localnetworkgateway-module-owners-bicep
358avm/res/network/nat-gateway46d3xbcp.res.network-natgatewayavm-res-network-natgateway-module-owners-bicep
359avm/res/network/network-interface46d3xbcp.res.network-networkinterfaceavm-res-network-networkinterface-module-owners-bicep
360avm/res/network/network-manager46d3xbcp.res.network-networkmanageravm-res-network-networkmanager-module-owners-bicep
361avm/res/network/network-manager/connectivity-configuration46d3xbcp.res.network-nwmgr-connectivityconfigavm-res-network-networkmanager-module-owners-bicep
362avm/res/network/network-manager/network-group46d3xbcp.res.network-nwmgr-networkgroupavm-res-network-networkmanager-module-owners-bicep
363avm/res/network/network-manager/network-group/static-member46d3xbcp.res.network-nwmgr-nwgrpstaticmemberavm-res-network-networkmanager-module-owners-bicep
364avm/res/network/network-manager/routing-configuration46d3xbcp.res.network-nwmgr-routingcfgavm-res-network-networkmanager-module-owners-bicep
365avm/res/network/network-manager/routing-configuration/rule-collection46d3xbcp.res.network-nwmgr-routingcfgrulecollavm-res-network-networkmanager-module-owners-bicep
366avm/res/network/network-manager/routing-configuration/rule-collection/rule46d3xbcp.res.network-nwmgr-routingcfgrulecollruleavm-res-network-networkmanager-module-owners-bicep
367avm/res/network/network-manager/scope-connection46d3xbcp.res.network-nwmgr-scopeconnectionavm-res-network-networkmanager-module-owners-bicep
368avm/res/network/network-manager/security-admin-configuration46d3xbcp.res.network-nwmgr-secadmcfgavm-res-network-networkmanager-module-owners-bicep
369avm/res/network/network-manager/security-admin-configuration/rule-collection46d3xbcp.res.network-nwmgr-secadmcfgrulecollavm-res-network-networkmanager-module-owners-bicep
370avm/res/network/network-manager/security-admin-configuration/rule-collection/rule46d3xbcp.res.network-nwmgr-secadmcfgrulecollruleavm-res-network-networkmanager-module-owners-bicep
371avm/res/network/network-security-group46d3xbcp.res.network-networksecuritygroupavm-res-network-networksecuritygroup-module-owners-bicep
372avm/res/network/network-security-perimeter46d3xbcp.res.network-nwsecurityperimeteravm-res-network-networksecurityperimeter-module-owners-bicep
373avm/res/network/network-security-perimeter/profile46d3xbcp.res.network-nwsecperim-profileavm-res-network-networksecurityperimeter-module-owners-bicep
374avm/res/network/network-security-perimeter/profile/access-rule46d3xbcp.res.network-nwsecperim-profileaccessruleavm-res-network-networksecurityperimeter-module-owners-bicep
375avm/res/network/network-watcher46d3xbcp.res.network-networkwatcheravm-res-network-networkwatcher-module-owners-bicep
376avm/res/network/network-watcher/connection-monitor46d3xbcp.res.network-networkwatcher-connmonitoravm-res-network-networkwatcher-module-owners-bicep
377avm/res/network/network-watcher/flow-log46d3xbcp.res.network-networkwatcher-flowlogavm-res-network-networkwatcher-module-owners-bicep
378avm/res/network/p2s-vpn-gateway46d3xbcp.res.network-p2svpngatewayavm-res-network-p2svpngateway-module-owners-bicep
379avm/res/network/private-dns-zone46d3xbcp.res.network-privatednszoneavm-res-network-privatednszone-module-owners-bicep
380avm/res/network/private-dns-zone/a46d3xbcp.res.nw-privdnszoneaavm-res-network-privatednszone-module-owners-bicep
381avm/res/network/private-dns-zone/aaaa46d3xbcp.res.nw-privdnszoneaaaaavm-res-network-privatednszone-module-owners-bicep
382avm/res/network/private-dns-zone/cname46d3xbcp.res.nw-privdnszonecnameavm-res-network-privatednszone-module-owners-bicep
383avm/res/network/private-dns-zone/mx46d3xbcp.res.nw-privdnszonemxavm-res-network-privatednszone-module-owners-bicep
384avm/res/network/private-dns-zone/ptr46d3xbcp.res.nw-privdnszoneptravm-res-network-privatednszone-module-owners-bicep
385avm/res/network/private-dns-zone/soa46d3xbcp.res.nw-privdnszonesoaavm-res-network-privatednszone-module-owners-bicep
386avm/res/network/private-dns-zone/srv46d3xbcp.res.nw-privdnszonesrvavm-res-network-privatednszone-module-owners-bicep
387avm/res/network/private-dns-zone/txt46d3xbcp.res.nw-privdnszonetxtavm-res-network-privatednszone-module-owners-bicep
388avm/res/network/private-dns-zone/virtual-network-link46d3xbcp.res.nw-privdnszonevnetlinkavm-res-network-privatednszone-module-owners-bicep
389avm/res/network/private-endpoint46d3xbcp.res.network-privateendpointavm-res-network-privateendpoint-module-owners-bicep
390avm/res/network/private-endpoint/private-dns-zone-group46d3xbcp.res.network-privendpoint-privdnszonegrpavm-res-network-privateendpoint-module-owners-bicep
391avm/res/network/private-link-service46d3xbcp.res.network-privatelinkserviceavm-res-network-privatelinkservice-module-owners-bicep
392avm/res/network/public-ip-address46d3xbcp.res.network-publicipaddressavm-res-network-publicipaddress-module-owners-bicep
393avm/res/network/public-ip-prefix46d3xbcp.res.network-publicipprefixavm-res-network-publicipprefix-module-owners-bicep
394avm/res/network/route-table46d3xbcp.res.network-routetableavm-res-network-routetable-module-owners-bicep
395avm/res/network/service-endpoint-policy46d3xbcp.res.network-serviceendpointpolicyavm-res-network-serviceendpointpolicy-module-owners-bicep
396avm/res/network/trafficmanagerprofile46d3xbcp.res.network-trafficmanagerprofileavm-res-network-trafficmanagerprofile-module-owners-bicep
397avm/res/network/virtual-hub46d3xbcp.res.network-virtualhubavm-res-network-virtualhub-module-owners-bicep
398avm/res/network/virtual-hub/hub-route-table46d3xbcp.res.network-virtualhub-hubroutetableavm-res-network-virtualhub-module-owners-bicep
399avm/res/network/virtual-hub/hub-virtual-network-connection46d3xbcp.res.network-virtualhub-hubvirtualnetworkavm-res-network-virtualhub-module-owners-bicep
400avm/res/network/virtual-hub/route-map46d3xbcp.res.network-virtualhubroutemapavm-res-network-virtualhub-module-owners-bicep
401avm/res/network/virtual-hub/routing-intent46d3xbcp.res.network-virtualhub-routingintentavm-res-network-virtualhub-module-owners-bicep
402avm/res/network/virtual-network46d3xbcp.res.network-virtualnetworkavm-res-network-virtualnetwork-module-owners-bicep
403avm/res/network/virtual-network-gateway46d3xbcp.res.network-virtualnetworkgatewayavm-res-network-virtualnetworkgateway-module-owners-bicep
404avm/res/network/virtual-network-gateway/nat-rule46d3xbcp.res.network-vnetgw-natruleavm-res-network-virtualnetworkgateway-module-owners-bicep
405avm/res/network/virtual-network/subnet46d3xbcp.res.network-virtualnetworksubnetavm-res-network-virtualnetwork-module-owners-bicep
406avm/res/network/virtual-network/virtual-network-peering46d3xbcp.res.network-virtualnetwork-peeringavm-res-network-virtualnetwork-module-owners-bicep
407avm/res/network/virtual-wan46d3xbcp.res.network-virtualwanavm-res-network-virtualwan-module-owners-bicep
408avm/res/network/vpn-gateway46d3xbcp.res.network-vpngatewayavm-res-network-vpngateway-module-owners-bicep
409avm/res/network/vpn-gateway/nat-rule46d3xbcp.res.network-vpngateway-natruleavm-res-network-vpngateway-module-owners-bicep
410avm/res/network/vpn-gateway/vpn-connection46d3xbcp.res.network-vpngateway-vpnconnectionavm-res-network-vpngateway-module-owners-bicep
411avm/res/network/vpn-server-configuration46d3xbcp.res.network-vpnserverconfigurationavm-res-network-vpnserverconfiguration-module-owners-bicep
412avm/res/network/vpn-site46d3xbcp.res.network-vpnsiteavm-res-network-vpnsite-module-owners-bicep
413avm/res/operational-insights/cluster46d3xbcp.res.operationalinsights-clusteravm-res-operationalinsights-cluster-module-owners-bicep
414avm/res/operational-insights/workspace46d3xbcp.res.operationalinsights-workspaceavm-res-operationalinsights-workspace-module-owners-bicep
415avm/res/operational-insights/workspace/data-export46d3xbcp.res.opins-worksp-dataexportavm-res-operationalinsights-workspace-module-owners-bicep
416avm/res/operational-insights/workspace/data-source46d3xbcp.res.opins-worksp-datasourceavm-res-operationalinsights-workspace-module-owners-bicep
417avm/res/operational-insights/workspace/linked-service46d3xbcp.res.opins-worksp-linkedserviceavm-res-operationalinsights-workspace-module-owners-bicep
418avm/res/operational-insights/workspace/linked-storage-account46d3xbcp.res.opins-worksp-linkedstgaccountavm-res-operationalinsights-workspace-module-owners-bicep
419avm/res/operational-insights/workspace/saved-search46d3xbcp.res.opins-worksp-savedsearchavm-res-operationalinsights-workspace-module-owners-bicep
420avm/res/operational-insights/workspace/storage-insight-config46d3xbcp.res.opins-worksp-stginsightconfigavm-res-operationalinsights-workspace-module-owners-bicep
421avm/res/operational-insights/workspace/table46d3xbcp.res.opins-worksp-tableavm-res-operationalinsights-workspace-module-owners-bicep
422avm/res/operations-management/solution46d3xbcp.res.operationsmanagement-solutionavm-res-operationsmanagement-solution-module-owners-bicep
423avm/res/portal/dashboard46d3xbcp.res.portal-dashboardavm-res-portal-dashboard-module-owners-bicep
424avm/res/power-bi-dedicated/capacity46d3xbcp.res.powerbidedicated-capacityavm-res-powerbidedicated-capacity-module-owners-bicep
425avm/res/purview/account46d3xbcp.res.purview-accountavm-res-purview-account-module-owners-bicep
426avm/res/recovery-services/vault46d3xbcp.res.recoveryservices-vaultavm-res-recoveryservices-vault-module-owners-bicep
427avm/res/recovery-services/vault/backup-config46d3xbcp.res.recsvcs-vault-backupconfigavm-res-recoveryservices-vault-module-owners-bicep
428avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item46d3xbcp.res.recsvcs-vault-bckpfabprotcontprotitmavm-res-recoveryservices-vault-module-owners-bicep
429avm/res/recovery-services/vault/backup-policy46d3xbcp.res.recsvcs-vault-backuppolicyavm-res-recoveryservices-vault-module-owners-bicep
430avm/res/recovery-services/vault/replication-alert-setting46d3xbcp.res.recsvcs-vault-replalertsettingavm-res-recoveryservices-vault-module-owners-bicep
431avm/res/recovery-services/vault/replication-fabric46d3xbcp.res.recsvcs-vault-repfabavm-res-recoveryservices-vault-module-owners-bicep
432avm/res/recovery-services/vault/replication-fabric/replication-protection-container46d3xbcp.res.recsvcs-vault-repfabrepprotcontaineravm-res-recoveryservices-vault-module-owners-bicep
433avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping46d3xbcp.res.recsvcs-vault-repfabrepprotcontmapavm-res-recoveryservices-vault-module-owners-bicep
434avm/res/recovery-services/vault/replication-policy46d3xbcp.res.recsvcs-vault-replicationpolicyavm-res-recoveryservices-vault-module-owners-bicep
435avm/res/relay/namespace46d3xbcp.res.relay-namespaceavm-res-relay-namespace-module-owners-bicep
436avm/res/relay/namespace/authorization-rule46d3xbcp.res.relay-namespace-authorizationruleavm-res-relay-namespace-module-owners-bicep
437avm/res/relay/namespace/hybrid-connection46d3xbcp.res.relay-namespace-hybconnavm-res-relay-namespace-module-owners-bicep
438avm/res/relay/namespace/hybrid-connection/authorization-rule46d3xbcp.res.relay-namespace-hybconnautzruleavm-res-relay-namespace-module-owners-bicep
439avm/res/relay/namespace/network-rule-set46d3xbcp.res.relay-namespace-networkrulesetavm-res-relay-namespace-module-owners-bicep
440avm/res/relay/namespace/wcf-relay46d3xbcp.res.relay-namespace-wcfrelayavm-res-relay-namespace-module-owners-bicep
441avm/res/relay/namespace/wcf-relay/authorization-rule46d3xbcp.res.relay-namespace-wcfrelayauthzruleavm-res-relay-namespace-module-owners-bicep
442avm/res/resource-graph/query46d3xbcp.resourcegraph-queryavm-res-resourcegraph-query-module-owners-bicep
443avm/res/resources/deployment-script46d3xbcp.res.resources-deploymentscriptavm-res-resources-deploymentscript-module-owners-bicep
444avm/res/resources/resource-group46d3xbcp.res.resources-resourcegroupavm-res-resources-resourcegroup-module-owners-bicep
445avm/res/scom/managed-instance46d3xbcp.res.scom-managedinstanceavm-res-scom-managedinstance-module-owners-bicep
446avm/res/search/search-service46d3xbcp.res.search-searchserviceavm-res-search-searchservice-module-owners-bicep
447avm/res/search/search-service/shared-private-link-resource46d3xbcp.res.search-searchsvc-sharedprivlinkresavm-res-search-searchservice-module-owners-bicep
448avm/res/security-insights/data-connector46d3xbcp.res.securityinsights-dataconnectoravm-res-securityinsights-dataconnector-module-owners-bicep
449avm/res/security-insights/onboarding-state46d3xbcp.res.securityinsights-onboardingstateavm-res-securityinsights-onboardingstate-module-owners-bicep
450avm/res/security-insights/setting46d3xbcp.res.securityinsights-settingavm-res-securityinsights-setting-module-owners-bicep
451avm/res/service-bus/namespace46d3xbcp.res.servicebus-namespaceavm-res-servicebus-namespace-module-owners-bicep
452avm/res/service-bus/namespace/authorization-rule46d3xbcp.res.servicebus-namespace-authzruleavm-res-servicebus-namespace-module-owners-bicep
453avm/res/service-bus/namespace/disaster-recovery-config46d3xbcp.res.servicebus-namespace-drconfigavm-res-servicebus-namespace-module-owners-bicep
454avm/res/service-bus/namespace/migration-configuration46d3xbcp.res.servicebus-namespace-migrconfigavm-res-servicebus-namespace-module-owners-bicep
455avm/res/service-bus/namespace/network-rule-set46d3xbcp.res.servicebus-namespace-netrulesetavm-res-servicebus-namespace-module-owners-bicep
456avm/res/service-bus/namespace/queue46d3xbcp.res.servicebus-namespacequeueavm-res-servicebus-namespace-module-owners-bicep
457avm/res/service-bus/namespace/queue/authorization-rule46d3xbcp.res.servicebus-namespace-queueauthzruleavm-res-servicebus-namespace-module-owners-bicep
458avm/res/service-bus/namespace/topic46d3xbcp.res.servicebus-namespacetopicavm-res-servicebus-namespace-module-owners-bicep
459avm/res/service-bus/namespace/topic/authorization-rule46d3xbcp.res.servicebus-namespace-topicauthzruleavm-res-servicebus-namespace-module-owners-bicep
460avm/res/service-bus/namespace/topic/subscription46d3xbcp.res.servicebus-namespace-topicsubavm-res-servicebus-namespace-module-owners-bicep
461avm/res/service-bus/namespace/topic/subscription/rule46d3xbcp.res.servicebus-namespace-topicsubruleavm-res-servicebus-namespace-module-owners-bicep
462avm/res/service-fabric/cluster46d3xbcp.res.servicefabric-clusteravm-res-servicefabric-cluster-module-owners-bicep
463avm/res/service-fabric/cluster/application-type46d3xbcp.res.servicefabric-cluster-apptypeavm-res-servicefabric-cluster-module-owners-bicep
464avm/res/service-networking/traffic-controller46d3xbcp.res.servicenetworking-trafficcontrolleravm-res-servicenetworking-trafficcontroller-module-owners-bicep
465avm/res/service-networking/traffic-controller/association46d3xbcp.res.servicenetworking-traffctrlassocavm-res-servicenetworking-trafficcontroller-module-owners-bicep
466avm/res/service-networking/traffic-controller/frontend46d3xbcp.res.servicenetworking-traffctrlfrontendavm-res-servicenetworking-trafficcontroller-module-owners-bicep
467avm/res/service-networking/traffic-controller/security-policy46d3xbcp.res.servicenetworking-traffctrlsecpolicyavm-res-servicenetworking-trafficcontroller-module-owners-bicep
468avm/res/signal-r-service/signal-r46d3xbcp.res.signalrservice-signalravm-res-signalrservice-signalr-module-owners-bicep
469avm/res/signal-r-service/web-pub-sub46d3xbcp.res.signalrservice-webpubsubavm-res-signalrservice-webpubsub-module-owners-bicep
470avm/res/sql-virtual-machine/sql-virtual-machine46d3xbcp.res.sqlvm-sqlvmavm-res-sqlvirtualmachine-sqlvirtualmachine-module-owners-bicep
471avm/res/sql/instance-pool46d3xbcp.res.sql-instancepoolavm-res-sql-instancepool-module-owners-bicep
472avm/res/sql/managed-instance46d3xbcp.res.sql-managedinstanceavm-res-sql-managedinstance-module-owners-bicep
473avm/res/sql/managed-instance/database46d3xbcp.res.sql-mi-databaseavm-res-sql-managedinstance-module-owners-bicep
474avm/res/sql/managed-instance/database/backup-long-term-retention-policy46d3xbcp.res.sql-mi-dbbckplongermretpolicyavm-res-sql-managedinstance-module-owners-bicep
475avm/res/sql/managed-instance/database/backup-short-term-retention-policy46d3xbcp.res.sql-mi-dbbckpshortermretpolicyavm-res-sql-managedinstance-module-owners-bicep
476avm/res/sql/managed-instance/encryption-protector46d3xbcp.res.sql-mi-encryptionprotecoravm-res-sql-managedinstance-module-owners-bicep
477avm/res/sql/managed-instance/key46d3xbcp.res.sql-mi-keyavm-res-sql-managedinstance-module-owners-bicep
478avm/res/sql/managed-instance/security-alert-policy46d3xbcp.res.sql-mi-securityalertpolicyavm-res-sql-managedinstance-module-owners-bicep
479avm/res/sql/managed-instance/vulnerability-assessment46d3xbcp.res.sql-mi-vulnerabilityassessmentavm-res-sql-managedinstance-module-owners-bicep
480avm/res/sql/server46d3xbcp.res.sql-serveravm-res-sql-server-module-owners-bicep
481avm/res/sql/server/auditing-setting46d3xbcp.res.sql-server-auditingsettingavm-res-sql-server-module-owners-bicep
482avm/res/sql/server/database46d3xbcp.res.sql-serverdbavm-res-sql-server-module-owners-bicep
483avm/res/sql/server/database/backup-long-term-retention-policy46d3xbcp.res.sql-server-dbbckplongtermretpolicyavm-res-sql-server-module-owners-bicep
484avm/res/sql/server/database/backup-short-term-retention-policy46d3xbcp.res.sql-server-dbbckpshorttermretpolicyavm-res-sql-server-module-owners-bicep
485avm/res/sql/server/elastic-pool46d3xbcp.res.sql-server-elasticpoolavm-res-sql-server-module-owners-bicep
486avm/res/sql/server/encryption-protector46d3xbcp.res.sql-server-encryptionprotectoravm-res-sql-server-module-owners-bicep
487avm/res/sql/server/failover-group46d3xbcp.res.sql-server-failovergroupavm-res-sql-server-module-owners-bicep
488avm/res/sql/server/firewall-rule46d3xbcp.res.sql-server-firewallruleavm-res-sql-server-module-owners-bicep
489avm/res/sql/server/key46d3xbcp.res.sql-server-keyavm-res-sql-server-module-owners-bicep
490avm/res/sql/server/security-alert-policy46d3xbcp.res.sql-server-securityalertpolicyavm-res-sql-server-module-owners-bicep
491avm/res/sql/server/virtual-network-rule46d3xbcp.res.sql-server-virtualnetworkruleavm-res-sql-server-module-owners-bicep
492avm/res/sql/server/vulnerability-assessment46d3xbcp.res.sql-server-vulnerabilityassessmentavm-res-sql-server-module-owners-bicep
493avm/res/storage/storage-account46d3xbcp.res.storage-storageaccountavm-res-storage-storageaccount-module-owners-bicep
494avm/res/storage/storage-account/blob-service46d3xbcp.res.storage-storageaccount-blobserviceavm-res-storage-storageaccount-module-owners-bicep
495avm/res/storage/storage-account/blob-service/container46d3xbcp.res.storage-blobcontaineravm-res-storage-storageaccount-module-owners-bicep
496avm/res/storage/storage-account/blob-service/container/immutability-policy46d3xbcp.res.storage-containerimmutpolicyavm-res-storage-storageaccount-module-owners-bicep
497avm/res/storage/storage-account/file-service46d3xbcp.res.storage-storageaccount-fileserviceavm-res-storage-storageaccount-module-owners-bicep
498avm/res/storage/storage-account/file-service/share46d3xbcp.res.storage-fileshareavm-res-storage-storageaccount-module-owners-bicep
499avm/res/storage/storage-account/local-user46d3xbcp.res.storage-localuseravm-res-storage-storageaccount-module-owners-bicep
500avm/res/storage/storage-account/management-policy46d3xbcp.res.storage-mgmtpolicyavm-res-storage-storageaccount-module-owners-bicep
501avm/res/storage/storage-account/object-replication-policy46d3xbcp.res.storage-storacct-objectreplpolicyavm-res-storage-storageaccount-module-owners-bicep
502avm/res/storage/storage-account/object-replication-policy/policy46d3xbcp.res.storage-storacct-objectreplpolicypolavm-res-storage-storageaccount-module-owners-bicep
503avm/res/storage/storage-account/queue-service46d3xbcp.res.storage-storageaccount-queueserviceavm-res-storage-storageaccount-module-owners-bicep
504avm/res/storage/storage-account/queue-service/queue46d3xbcp.res.storage-queueavm-res-storage-storageaccount-module-owners-bicep
505avm/res/storage/storage-account/table-service46d3xbcp.res.storage-storageaccount-tableserviceavm-res-storage-storageaccount-module-owners-bicep
506avm/res/storage/storage-account/table-service/table46d3xbcp.res.storage-tableavm-res-storage-storageaccount-module-owners-bicep
507avm/res/stream-analytics/streaming-job46d3xbcp.res.streamanalytics-streamingjobavm-res-streamanalytics-streamingjob-module-owners-bicep
508avm/res/synapse/private-link-hub46d3xbcp.res.synapse-privatelinkhubavm-res-synapse-privatelinkhub-module-owners-bicep
509avm/res/synapse/workspace46d3xbcp.res.synapse-workspaceavm-res-synapse-workspace-module-owners-bicep
510avm/res/synapse/workspace/administrator46d3xbcp.res.synapse-workspace-administratoravm-res-synapse-workspace-module-owners-bicep
511avm/res/synapse/workspace/big-data-pool46d3xbcp.res.synapse-workspace-bigdatapoolavm-res-synapse-workspace-module-owners-bicep
512avm/res/synapse/workspace/firewall-rule46d3xbcp.res.synapse-workspace-firewallruleavm-res-synapse-workspace-module-owners-bicep
513avm/res/synapse/workspace/integration-runtime46d3xbcp.res.synapse-workspace-integrationruntimeavm-res-synapse-workspace-module-owners-bicep
514avm/res/synapse/workspace/key46d3xbcp.res.synapse-workspace-keyavm-res-synapse-workspace-module-owners-bicep
515avm/res/synapse/workspace/sql-pool46d3xbcp.res.synapse-workspace-sqlpoolavm-res-synapse-workspace-module-owners-bicep
516avm/res/virtual-machine-images/image-template46d3xbcp.res.virtualmachineimages-imagetemplateavm-res-virtualmachineimages-imagetemplate-module-owners-bicep
517avm/res/web/connection46d3xbcp.res.web-connectionavm-res-web-connection-module-owners-bicep
518avm/res/web/hosting-environment46d3xbcp.res.web-hostingenvironmentavm-res-web-hostingenvironment-module-owners-bicep
519avm/res/web/hosting-environment/configuration46d3xbcp.res.web-hostingenvironment-configurationavm-res-web-hostingenvironment-module-owners-bicep
520avm/res/web/serverfarm46d3xbcp.res.web-serverfarmavm-res-web-serverfarm-module-owners-bicep
521avm/res/web/site46d3xbcp.res.web-siteavm-res-web-site-module-owners-bicep
522avm/res/web/site/basic-publishing-credentials-policy46d3xbcp.res.web-site-basicpublishingcredpolicyavm-res-web-site-module-owners-bicep
523avm/res/web/site/config46d3xbcp.res.web-siteconfigavm-res-web-site-module-owners-bicep
524avm/res/web/site/extension46d3xbcp.res.web-site-extensionavm-res-web-site-module-owners-bicep
525avm/res/web/site/hybrid-connection-namespace/relay46d3xbcp.res.web-site-hybconnnamespacerelayavm-res-web-site-module-owners-bicep
526avm/res/web/site/slot46d3xbcp.res.web-siteslotavm-res-web-site-module-owners-bicep
527avm/res/web/site/slot/basic-publishing-credentials-policy46d3xbcp.res.web-site-slotbasicpubcredpolicyavm-res-web-site-module-owners-bicep
528avm/res/web/site/slot/config46d3xbcp.res.web-site-slotconfigavm-res-web-site-module-owners-bicep
529avm/res/web/site/slot/extension46d3xbcp.res.web-site-slotextensionavm-res-web-site-module-owners-bicep
530avm/res/web/site/slot/hybrid-connection-namespace/relay46d3xbcp.res.web-site-slothybconnnamespacerelayavm-res-web-site-module-owners-bicep
531avm/res/web/static-site46d3xbcp.res.web-staticsiteavm-res-web-staticsite-module-owners-bicep
532avm/res/web/static-site/config46d3xbcp.res.web-staticsite-configavm-res-web-staticsite-module-owners-bicep
533avm/res/web/static-site/custom-domain46d3xbcp.res.web-staticsite-customdomainavm-res-web-staticsite-module-owners-bicep
534avm/res/web/static-site/linked-backend46d3xbcp.res.web-staticsite-linkedbackendavm-res-web-staticsite-module-owners-bicep

Bicep Pattern Modules

Module catalog

LanguageClassificationPublished 🟢 & 🟡Proposed ⚪SUM
BicepPattern412061
➕ Additional information
Legend

Summary of status icons used on this page

IconStatusDescription
Proposed modulesModules that are proposed and/or being worked on but not published yet.
🟢 & 🟡Published modulesAvailable (🟢) and Orphaned (🟡) modules that are active and usable.
🔴Deprecated modulesModules that reached the end of their lifecycle.
📇All modulesIncluding Published, Proposed and Deprecated ones.

See the Module Lifecycle page for more details.

Info

This page contains various views of the module index (catalog) for Bicep Pattern Modules. To see these views, click on the expandable sections with the “➕” sign below.

  • To see the full, unfiltered, unformatted module index on GitHub, click here.

  • To download the source CSV file, click here.

Note

Modules listed below that aren’t shown with the status of Module Available 🟢, are currently in development and are not yet available for use. For proposed modules, see the Proposed modules section below.

Published modules - 🟢 & 🟡

➕ Published Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/aca-lza/hosting-environmentAzure Container Apps (ACA) LZA - Hosting Environmentkpantos
Konstantinos Pantos
02avm/ptn/ai-ml/ai-foundryAI-ML - AI Foundrymswantek68
Mike Swantek
sethsteenken
Seth Steenken
03avm/ptn/ai-platform/baselineAI Platform - Baselinececheta
Chinedum Echeta
ross-p-smith
Ross Smith
04avm/ptn/alz/amaAzure Landing Zones (ALZ) - Azure Monitoring Agent (AMA)oZakari
Zach Trocinski
05avm/ptn/alz/emptyAzure Landing Zones (ALZ) - Emptyjtracey93
Jack Tracey
oZakari
Zach Trocinski
06avm/ptn/app-service-lza/hosting-environmentApp Service LZA - Hosting EnvironmentMikeTB-Microsoft
Michael Baker
07avm/ptn/app/container-job-toolkitContainer Job ToolkitReneHezser
Rene Hezser
08avm/ptn/app/iaas-vm-cosmosdb-tier4Workload - IaaS VM Cosmos DB - Tier 4mikestiers
Mike Stiers
09avm/ptn/authorization/pim-role-assignmentAuthorization - PIM Role Assignmentsebassem
Seif Bassem
10avm/ptn/authorization/policy-assignmentAuthorization - Policy Assignmentjtracey93
Jack Tracey
11avm/ptn/authorization/policy-exemptionAuthorization - Policy ExemptionoZakari
Zach Trocinski
12avm/ptn/authorization/resource-role-assignmentAuthorization - Resource Role Assignmentpeterbud
Peter Budai
13avm/ptn/authorization/role-assignmentAuthorization - Role Assignmentjtracey93
Jack Tracey
14avm/ptn/authorization/role-definitionAuthorization - Role Definitionjtracey93
Jack Tracey
15avm/ptn/azd/acr-container-appAZD - ACR Container App
Azure Developer CLI - ACR Container App
JeffreyCA
Jeffrey Chen
16avm/ptn/azd/aksAZD - AKS
Azure Developer CLI - Azure Kubernetes Services
JeffreyCA
Jeffrey Chen
17avm/ptn/azd/aks-automatic-clusterAZD - AKS Automatic Cluster
Azure Developer CLI - Azure Kubernetes Services Automatic Cluster
JeffreyCA
Jeffrey Chen
18avm/ptn/azd/apim-apiAZD - APIM API
Azure Developer CLI - API Management
JeffreyCA
Jeffrey Chen
19avm/ptn/azd/container-app-upsertAZD - Container App Upsert
Azure Developer CLI - Container Apps Upsert
JeffreyCA
Jeffrey Chen
20avm/ptn/azd/container-apps-stackAZD - Container Apps Stack
Azure Developer CLI - Container Apps Stack
JeffreyCA
Jeffrey Chen
21avm/ptn/azd/insights-dashboardAZD - Insights Dashboard
Azure Developer CLI - Inishgts Dashboard
JeffreyCA
Jeffrey Chen
22avm/ptn/azd/monitoringAZD - Monitoring
Azure Developer CLI - Monitoring
JeffreyCA
Jeffrey Chen
23avm/ptn/data/private-analytical-workspacePrivate Analytical Workspace
Data Analytics, Data Lake, Databricks, Database
jbinko
Jiri Binko
24avm/ptn/deployment-script/import-image-to-acrDeployment Script - Import Container Image to ACRReneHezser
Rene Hezser
25avm/ptn/dev-ops/cicd-agents-and-runnersAzure DevOps and GitHub CI/CD Agents and Runnerssebassem
Seif Bassem
26avm/ptn/finops-toolkit/finops-hubFinOps Toolkit - FinOps HubFallenHoot
Zach Olinske
27avm/ptn/lz/sub-vendingLanding Zone Subscription Vendingjtracey93
Jack Tracey
sebassem
Seif Bassem
28avm/ptn/mgmt-groups/subscription-placementManagement Groups - Subscription PlacementoZakari
Zach Trocinski
29avm/ptn/network/hub-networkingHub Networking
30avm/ptn/network/private-link-private-dns-zonesPrivate Link Private DNS Zonesjtracey93
Jack Tracey
31avm/ptn/network/virtual-wanVirtual WAN
vWAN
ericscheffler
Eric Scheffler
juancj
Juan Jimenez
32avm/ptn/policy-insights/remediationPolicy Insights Remediationdonk-msft
Don Koning
33avm/ptn/sa/chat-with-your-dataSA - Chat with your data
Solution Accelerator - Chat with your data (CWYD)
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz
34avm/ptn/sa/content-processingSA - Content Processing
Solution Accelerator - Content Processing
brittneek
Brittnee Keller
35avm/ptn/sa/conversation-knowledge-miningSA - Conversation knowledge mining
Solution Accelerator - Conversation knowledge mining (CKM)
alguadam
Alvaro Guadamillas Herranz
36avm/ptn/sa/document-knowledge-miningSA - Document knowledge mining
Solution Accelerator - Document knowledge mining (DKM)
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz
37avm/ptn/sa/modernize-your-codeSA - Modernize your code
Solution Accelerator - Modernize your code
sethsteenken
Seth Steenken
38avm/ptn/sa/multi-agent-custom-automation-engineSA - Multi Agent Custom Automation Engine
Solution Accelerator - Multi Agent Custom Automation Engine
alguadam
Alvaro Guadamillas Herranz
39avm/ptn/security/security-centerAzure Security Center (Defender for Cloud)oZakari
Zach Trocinski
jtracey93
Jack Tracey
40avm/ptn/subscription/service-health-alertsService Health Alertssebassem
Seif Bassem
41avm/ptn/virtual-machine-images/azure-image-builderCustom Images using Azure Image BuilderAlexanderSehr
Alexander Sehr

Proposed modules - ⚪

➕ Proposed Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/ai-ml/landing-zoneAI-ML - Landing Zone (LZ)placerda
Paulo Lacerda
mbilalamjad
Bilal Amjad
02avm/ptn/app/cosmos-db-account-container-appCosmos DB Account - Container App
03avm/ptn/app/mongodb-cluster-container-appMongoDB Cluster - Container App
04avm/ptn/app/paas-ase-cosmosdb-tier4Workload - PaaS ASE Cosmos DB - Tier 4mikestiers
Mike Stiers
05avm/ptn/avd-lza/insightsAzure Virtual Desktop (AVD) LZA - Insights
06avm/ptn/avd-lza/management-planeAzure Virtual Desktop (AVD) LZA - Management Plane
07avm/ptn/avd-lza/networkingAzure Virtual Desktop (AVD) LZA - Networking
08avm/ptn/avd-lza/session-hostsAzure Virtual Desktop (AVD) LZA - Session Hosts
09avm/ptn/deployment-script/create-kv-ssh-key-pairDeployment Script - Create Key Vault SSH Key Pairvlahane
Vishal Lahane
10avm/ptn/deployment-script/privateDeployment Script - Private Scriptsebassem
Seif Bassem
11avm/ptn/dev-center/dev-boxDev-Boxtimfurnival-MSFT
Tim Furnival
12avm/ptn/lza-shared/data-servicesLZA Shared - Data Services
Landing Zone Accelerators - Shared - Data Services
kpantos
Konstantinos Pantos
13avm/ptn/maintenance/azure-update-managerAzure Update Managerakhilthomas011
Akhil Thomas
14avm/ptn/monitoring/ambaAzure Monitor Baseline Alerts (AMBA)arjenhuitema
Arjen Huitema
15avm/ptn/monitoring/amba-alzAzure Monitor Baseline Alerts (AMBA) - ALZarjenhuitema
Arjen Huitema
16avm/ptn/network/vwan-connected-vnetsVNETs peered to Virtual WANjuancj
Juan Jimenez
ericscheffler
Eric Scheffler
17avm/ptn/openai/e2e-baselineAzure OpenAI End-to-End Baseline Implementation
18avm/ptn/sa/content-generationSA - Content Generation
Solution Accelerator - Content Generation
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz
19avm/ptn/sa/customer-chatbotSA - Bring your own Customer Chatbot
Solution Accelerator - Bring your own Customer Chatbot (BYOCC)
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz
20avm/ptn/security/sentinelSentinel

Deprecated modules - 🔴

➕ Deprecated Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/azd/ml-ai-environmentAZD - ML AI Environment
Azure Developer CLI - Machine Learning AI Environment

02avm/ptn/azd/ml-hub-dependenciesAZD - ML Hub Dependencies
Azure Developer CLI - Machine Learning Hub Dependencies

03avm/ptn/azd/ml-projectAZD - ML Project
Azure Developer CLI - Machine Learning Project

04avm/ptn/sa/build-your-own-copilotSA - Build your own Copilot
Solution Accelerator - Build your own Copilot (BYOC)


All modules - 📇

➕ All Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/aca-lza/hosting-environmentAzure Container Apps (ACA) LZA - Hosting Environmentkpantos
Konstantinos Pantos
02avm/ptn/ai-ml/ai-foundryAI-ML - AI Foundrymswantek68
Mike Swantek
sethsteenken
Seth Steenken
03avm/ptn/ai-ml/landing-zoneAI-ML - Landing Zone (LZ)placerda
Paulo Lacerda
mbilalamjad
Bilal Amjad
04avm/ptn/ai-platform/baselineAI Platform - Baselinececheta
Chinedum Echeta
ross-p-smith
Ross Smith
05avm/ptn/alz/amaAzure Landing Zones (ALZ) - Azure Monitoring Agent (AMA)oZakari
Zach Trocinski
06avm/ptn/alz/emptyAzure Landing Zones (ALZ) - Emptyjtracey93
Jack Tracey
oZakari
Zach Trocinski
07avm/ptn/app-service-lza/hosting-environmentApp Service LZA - Hosting EnvironmentMikeTB-Microsoft
Michael Baker
08avm/ptn/app/container-job-toolkitContainer Job ToolkitReneHezser
Rene Hezser
09avm/ptn/app/cosmos-db-account-container-appCosmos DB Account - Container App
10avm/ptn/app/iaas-vm-cosmosdb-tier4Workload - IaaS VM Cosmos DB - Tier 4mikestiers
Mike Stiers
11avm/ptn/app/mongodb-cluster-container-appMongoDB Cluster - Container App
12avm/ptn/app/paas-ase-cosmosdb-tier4Workload - PaaS ASE Cosmos DB - Tier 4mikestiers
Mike Stiers
13avm/ptn/authorization/pim-role-assignmentAuthorization - PIM Role Assignmentsebassem
Seif Bassem
14avm/ptn/authorization/policy-assignmentAuthorization - Policy Assignmentjtracey93
Jack Tracey
15avm/ptn/authorization/policy-exemptionAuthorization - Policy ExemptionoZakari
Zach Trocinski
16avm/ptn/authorization/resource-role-assignmentAuthorization - Resource Role Assignmentpeterbud
Peter Budai
17avm/ptn/authorization/role-assignmentAuthorization - Role Assignmentjtracey93
Jack Tracey
18avm/ptn/authorization/role-definitionAuthorization - Role Definitionjtracey93
Jack Tracey
19avm/ptn/avd-lza/insightsAzure Virtual Desktop (AVD) LZA - Insights
20avm/ptn/avd-lza/management-planeAzure Virtual Desktop (AVD) LZA - Management Plane
21avm/ptn/avd-lza/networkingAzure Virtual Desktop (AVD) LZA - Networking
22avm/ptn/avd-lza/session-hostsAzure Virtual Desktop (AVD) LZA - Session Hosts
23avm/ptn/azd/acr-container-appAZD - ACR Container App
Azure Developer CLI - ACR Container App
JeffreyCA
Jeffrey Chen
24avm/ptn/azd/aksAZD - AKS
Azure Developer CLI - Azure Kubernetes Services
JeffreyCA
Jeffrey Chen
25avm/ptn/azd/aks-automatic-clusterAZD - AKS Automatic Cluster
Azure Developer CLI - Azure Kubernetes Services Automatic Cluster
JeffreyCA
Jeffrey Chen
26avm/ptn/azd/apim-apiAZD - APIM API
Azure Developer CLI - API Management
JeffreyCA
Jeffrey Chen
27avm/ptn/azd/container-app-upsertAZD - Container App Upsert
Azure Developer CLI - Container Apps Upsert
JeffreyCA
Jeffrey Chen
28avm/ptn/azd/container-apps-stackAZD - Container Apps Stack
Azure Developer CLI - Container Apps Stack
JeffreyCA
Jeffrey Chen
29avm/ptn/azd/insights-dashboardAZD - Insights Dashboard
Azure Developer CLI - Inishgts Dashboard
JeffreyCA
Jeffrey Chen
30avm/ptn/azd/ml-ai-environmentAZD - ML AI Environment
Azure Developer CLI - Machine Learning AI Environment

31avm/ptn/azd/ml-hub-dependenciesAZD - ML Hub Dependencies
Azure Developer CLI - Machine Learning Hub Dependencies

32avm/ptn/azd/ml-projectAZD - ML Project
Azure Developer CLI - Machine Learning Project

33avm/ptn/azd/monitoringAZD - Monitoring
Azure Developer CLI - Monitoring
JeffreyCA
Jeffrey Chen
34avm/ptn/data/private-analytical-workspacePrivate Analytical Workspace
Data Analytics, Data Lake, Databricks, Database
jbinko
Jiri Binko
35avm/ptn/deployment-script/create-kv-ssh-key-pairDeployment Script - Create Key Vault SSH Key Pairvlahane
Vishal Lahane
36avm/ptn/deployment-script/import-image-to-acrDeployment Script - Import Container Image to ACRReneHezser
Rene Hezser
37avm/ptn/deployment-script/privateDeployment Script - Private Scriptsebassem
Seif Bassem
38avm/ptn/dev-center/dev-boxDev-Boxtimfurnival-MSFT
Tim Furnival
39avm/ptn/dev-ops/cicd-agents-and-runnersAzure DevOps and GitHub CI/CD Agents and Runnerssebassem
Seif Bassem
40avm/ptn/finops-toolkit/finops-hubFinOps Toolkit - FinOps HubFallenHoot
Zach Olinske
41avm/ptn/lz/sub-vendingLanding Zone Subscription Vendingjtracey93
Jack Tracey
sebassem
Seif Bassem
42avm/ptn/lza-shared/data-servicesLZA Shared - Data Services
Landing Zone Accelerators - Shared - Data Services
kpantos
Konstantinos Pantos
43avm/ptn/maintenance/azure-update-managerAzure Update Managerakhilthomas011
Akhil Thomas
44avm/ptn/mgmt-groups/subscription-placementManagement Groups - Subscription PlacementoZakari
Zach Trocinski
45avm/ptn/monitoring/ambaAzure Monitor Baseline Alerts (AMBA)arjenhuitema
Arjen Huitema
46avm/ptn/monitoring/amba-alzAzure Monitor Baseline Alerts (AMBA) - ALZarjenhuitema
Arjen Huitema
47avm/ptn/network/hub-networkingHub Networking
48avm/ptn/network/private-link-private-dns-zonesPrivate Link Private DNS Zonesjtracey93
Jack Tracey
49avm/ptn/network/virtual-wanVirtual WAN
vWAN
ericscheffler
Eric Scheffler
juancj
Juan Jimenez
50avm/ptn/network/vwan-connected-vnetsVNETs peered to Virtual WANjuancj
Juan Jimenez
ericscheffler
Eric Scheffler
51avm/ptn/openai/e2e-baselineAzure OpenAI End-to-End Baseline Implementation
52avm/ptn/policy-insights/remediationPolicy Insights Remediationdonk-msft
Don Koning
53avm/ptn/sa/build-your-own-copilotSA - Build your own Copilot
Solution Accelerator - Build your own Copilot (BYOC)

54avm/ptn/sa/chat-with-your-dataSA - Chat with your data
Solution Accelerator - Chat with your data (CWYD)
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz
55avm/ptn/sa/content-generationSA - Content Generation
Solution Accelerator - Content Generation
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz
56avm/ptn/sa/content-processingSA - Content Processing
Solution Accelerator - Content Processing
brittneek
Brittnee Keller
57avm/ptn/sa/conversation-knowledge-miningSA - Conversation knowledge mining
Solution Accelerator - Conversation knowledge mining (CKM)
alguadam
Alvaro Guadamillas Herranz
58avm/ptn/sa/customer-chatbotSA - Bring your own Customer Chatbot
Solution Accelerator - Bring your own Customer Chatbot (BYOCC)
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz
59avm/ptn/sa/document-knowledge-miningSA - Document knowledge mining
Solution Accelerator - Document knowledge mining (DKM)
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz
60avm/ptn/sa/modernize-your-codeSA - Modernize your code
Solution Accelerator - Modernize your code
sethsteenken
Seth Steenken
61avm/ptn/sa/multi-agent-custom-automation-engineSA - Multi Agent Custom Automation Engine
Solution Accelerator - Multi Agent Custom Automation Engine
alguadam
Alvaro Guadamillas Herranz
62avm/ptn/security/security-centerAzure Security Center (Defender for Cloud)oZakari
Zach Trocinski
jtracey93
Jack Tracey
63avm/ptn/security/sentinelSentinel
64avm/ptn/subscription/service-health-alertsService Health Alertssebassem
Seif Bassem
65avm/ptn/virtual-machine-images/azure-image-builderCustom Images using Azure Image BuilderAlexanderSehr
Alexander Sehr

Module Publication History - 📅

➕ Module Publication History - Module names, status and owners

Modules published in April 2026

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/network/virtual-wanVirtual WAN
vWAN
ericscheffler
Eric Scheffler
juancj
Juan Jimenez

Modules published in September 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/sa/build-your-own-copilotSA - Build your own Copilot
Solution Accelerator - Build your own Copilot (BYOC)

02avm/ptn/sa/chat-with-your-dataSA - Chat with your data
Solution Accelerator - Chat with your data (CWYD)
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz
03avm/ptn/sa/document-knowledge-miningSA - Document knowledge mining
Solution Accelerator - Document knowledge mining (DKM)
aniaroramsft
Anish Arora
alguadam
Alvaro Guadamillas Herranz

Modules published in August 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/alz/amaAzure Landing Zones (ALZ) - Azure Monitoring Agent (AMA)oZakari
Zach Trocinski

Modules published in July 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/ai-ml/ai-foundryAI-ML - AI Foundrymswantek68
Mike Swantek
sethsteenken
Seth Steenken
02avm/ptn/app/iaas-vm-cosmosdb-tier4Workload - IaaS VM Cosmos DB - Tier 4mikestiers
Mike Stiers
03avm/ptn/sa/content-processingSA - Content Processing
Solution Accelerator - Content Processing
brittneek
Brittnee Keller
04avm/ptn/sa/modernize-your-codeSA - Modernize your code
Solution Accelerator - Modernize your code
sethsteenken
Seth Steenken

Modules published in June 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/sa/multi-agent-custom-automation-engineSA - Multi Agent Custom Automation Engine
Solution Accelerator - Multi Agent Custom Automation Engine
alguadam
Alvaro Guadamillas Herranz
02avm/ptn/subscription/service-health-alertsService Health Alertssebassem
Seif Bassem

Modules published in April 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/alz/emptyAzure Landing Zones (ALZ) - Emptyjtracey93
Jack Tracey
oZakari
Zach Trocinski
02avm/ptn/app-service-lza/hosting-environmentApp Service LZA - Hosting EnvironmentMikeTB-Microsoft
Michael Baker

Modules published in March 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/sa/conversation-knowledge-miningSA - Conversation knowledge mining
Solution Accelerator - Conversation knowledge mining (CKM)
alguadam
Alvaro Guadamillas Herranz

Modules published in February 2025

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/authorization/pim-role-assignmentAuthorization - PIM Role Assignmentsebassem
Seif Bassem
02avm/ptn/mgmt-groups/subscription-placementManagement Groups - Subscription PlacementoZakari
Zach Trocinski

Modules published in December 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/app/container-job-toolkitContainer Job ToolkitReneHezser
Rene Hezser
02avm/ptn/authorization/policy-exemptionAuthorization - Policy ExemptionoZakari
Zach Trocinski
03avm/ptn/authorization/role-definitionAuthorization - Role Definitionjtracey93
Jack Tracey
04avm/ptn/azd/aks-automatic-clusterAZD - AKS Automatic Cluster
Azure Developer CLI - Azure Kubernetes Services Automatic Cluster
JeffreyCA
Jeffrey Chen

Modules published in October 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/azd/acr-container-appAZD - ACR Container App
Azure Developer CLI - ACR Container App
JeffreyCA
Jeffrey Chen
02avm/ptn/azd/aksAZD - AKS
Azure Developer CLI - Azure Kubernetes Services
JeffreyCA
Jeffrey Chen
03avm/ptn/azd/container-app-upsertAZD - Container App Upsert
Azure Developer CLI - Container Apps Upsert
JeffreyCA
Jeffrey Chen
04avm/ptn/azd/container-apps-stackAZD - Container Apps Stack
Azure Developer CLI - Container Apps Stack
JeffreyCA
Jeffrey Chen
05avm/ptn/azd/ml-ai-environmentAZD - ML AI Environment
Azure Developer CLI - Machine Learning AI Environment

06avm/ptn/azd/ml-hub-dependenciesAZD - ML Hub Dependencies
Azure Developer CLI - Machine Learning Hub Dependencies

07avm/ptn/azd/ml-projectAZD - ML Project
Azure Developer CLI - Machine Learning Project

08avm/ptn/azd/monitoringAZD - Monitoring
Azure Developer CLI - Monitoring
JeffreyCA
Jeffrey Chen
09avm/ptn/data/private-analytical-workspacePrivate Analytical Workspace
Data Analytics, Data Lake, Databricks, Database
jbinko
Jiri Binko

Modules published in September 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/azd/apim-apiAZD - APIM API
Azure Developer CLI - API Management
JeffreyCA
Jeffrey Chen
02avm/ptn/azd/insights-dashboardAZD - Insights Dashboard
Azure Developer CLI - Inishgts Dashboard
JeffreyCA
Jeffrey Chen
03avm/ptn/dev-ops/cicd-agents-and-runnersAzure DevOps and GitHub CI/CD Agents and Runnerssebassem
Seif Bassem
04avm/ptn/network/hub-networkingHub Networking
05avm/ptn/virtual-machine-images/azure-image-builderCustom Images using Azure Image BuilderAlexanderSehr
Alexander Sehr

Modules published in August 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/aca-lza/hosting-environmentAzure Container Apps (ACA) LZA - Hosting Environmentkpantos
Konstantinos Pantos

Modules published in July 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/ai-platform/baselineAI Platform - Baselinececheta
Chinedum Echeta
ross-p-smith
Ross Smith
02avm/ptn/deployment-script/import-image-to-acrDeployment Script - Import Container Image to ACRReneHezser
Rene Hezser
03avm/ptn/network/private-link-private-dns-zonesPrivate Link Private DNS Zonesjtracey93
Jack Tracey

Modules published in June 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/lz/sub-vendingLanding Zone Subscription Vendingjtracey93
Jack Tracey
sebassem
Seif Bassem

Modules published in May 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/authorization/resource-role-assignmentAuthorization - Resource Role Assignmentpeterbud
Peter Budai
02avm/ptn/finops-toolkit/finops-hubFinOps Toolkit - FinOps HubFallenHoot
Zach Olinske

Modules published in April 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/ptn/authorization/policy-assignmentAuthorization - Policy Assignmentjtracey93
Jack Tracey
02avm/ptn/authorization/role-assignmentAuthorization - Role Assignmentjtracey93
Jack Tracey
03avm/ptn/policy-insights/remediationPolicy Insights Remediationdonk-msft
Don Koning
04avm/ptn/security/security-centerAzure Security Center (Defender for Cloud)oZakari
Zach Trocinski
jtracey93
Jack Tracey

For Module Owners & Contributors

Note

This section is mainly intended for module owners and contributors as it contains information important for module development, such as telemetry ID prefix, and GitHub Teams for Owners.

Module name, Telemetry ID prefix, GitHub Teams for Owners

➕ All Modules - Module name, Telemetry ID prefix, GitHub Teams for Owners
No.Module NameTelemetry ID prefixGitHub Teams for Module Owners (@Azure org)
01avm/ptn/aca-lza/hosting-environment46d3xbcp.ptn.acalza-hostingenvironmentavm-ptn-acalza-hostingenvironment-module-owners-bicep
02avm/ptn/ai-ml/ai-foundry46d3xbcp.ptn.aiml-aifoundryavm-ptn-aiml-aifoundry-module-owners-bicep
03avm/ptn/ai-ml/landing-zone46d3xbcp.ptn.aiml-lzavm-ptn-aiml-landingzone-module-owners-bicep
04avm/ptn/ai-platform/baseline46d3xbcp.ptn.aiplatform-baselineavm-ptn-aiplatform-baseline-module-owners-bicep
05avm/ptn/alz/ama46d3xbcp.ptn.alz-amaavm-ptn-alz-ama-module-owners-bicep
06avm/ptn/alz/empty46d3xbcp.ptn.alz-emptyavm-ptn-alz-empty-module-owners-bicep
07avm/ptn/app-service-lza/hosting-environment46d3xbcp.ptn.appsvclza-hostingenvironmentavm-ptn-appservicelza-hostingenvironment-module-owners-bicep
08avm/ptn/app/container-job-toolkit46d3xbcp.ptn.app-containerjobtoolkitavm-ptn-app-containerjobtoolkit-module-owners-bicep
09avm/ptn/app/cosmos-db-account-container-app46d3xbcp.ptn.app-cosmosdbacctcontappavm-ptn-app-cosmosdbaccountcontainerapp-module-owners-bicep
10avm/ptn/app/iaas-vm-cosmosdb-tier446d3xbcp.ptn.app-iaasvmcosmosdbt4avm-ptn-app-iaasvmcosmosdbtier4-module-owners-bicep
11avm/ptn/app/mongodb-cluster-container-app46d3xbcp.ptn.app-mongodbclustcontappavm-ptn-app-mongodbclustercontainerapp-module-owners-bicep
12avm/ptn/app/paas-ase-cosmosdb-tier446d3xbcp.ptn.app-paasasecosmosdbt4avm-ptn-app-paasasecosmosdbtier4-module-owners-bicep
13avm/ptn/authorization/pim-role-assignment46d3xbcp.ptn.authorization-pimroleassignmentavm-ptn-authorization-pimroleassignment-module-owners-bicep
14avm/ptn/authorization/policy-assignment46d3xbcp.ptn.authorization-policyassignmentavm-ptn-authorization-policyassignment-module-owners-bicep
15avm/ptn/authorization/policy-exemption46d3xbcp.ptn.authorization-policyexemptionavm-ptn-authorization-policyexemption-module-owners-bicep
16avm/ptn/authorization/resource-role-assignment46d3xbcp.ptn.authorization-resourceroleassignmentavm-ptn-authorization-resourceroleassignment-module-owners-bicep
17avm/ptn/authorization/role-assignment46d3xbcp.ptn.authorization-roleassignmentavm-ptn-authorization-roleassignment-module-owners-bicep
18avm/ptn/authorization/role-definition46d3xbcp.ptn.authorization-roledefinitionavm-ptn-authorization-roledefinition-module-owners-bicep
19avm/ptn/avd-lza/insights46d3xbcp.ptn.avdlza-insightsavm-ptn-avdlza-insights-module-owners-bicep
20avm/ptn/avd-lza/management-plane46d3xbcp.ptn.avdlza-managementplaneavm-ptn-avdlza-managementplane-module-owners-bicep
21avm/ptn/avd-lza/networking46d3xbcp.ptn.avdlza-networkingavm-ptn-avdlza-networking-module-owners-bicep
22avm/ptn/avd-lza/session-hosts46d3xbcp.ptn.avdlza-sessionhostsavm-ptn-avdlza-sessionhosts-module-owners-bicep
23avm/ptn/azd/acr-container-app46d3xbcp.ptn.azd-acrcontainerappavm-ptn-azd-acrcontainerapp-module-owners-bicep
24avm/ptn/azd/aks46d3xbcp.ptn.azd-aksavm-ptn-azd-aks-module-owners-bicep
25avm/ptn/azd/aks-automatic-cluster46d3xbcp.ptn.azd-aksautomaticclusteravm-ptn-azd-aksautomaticcluster-module-owners-bicep
26avm/ptn/azd/apim-api46d3xbcp.ptn.azd-apimapiavm-ptn-azd-apimapi-module-owners-bicep
27avm/ptn/azd/container-app-upsert46d3xbcp.ptn.azd-containerappupsertavm-ptn-azd-containerappupsert-module-owners-bicep
28avm/ptn/azd/container-apps-stack46d3xbcp.ptn.azd-containerappsstackavm-ptn-azd-containerappsstack-module-owners-bicep
29avm/ptn/azd/insights-dashboard46d3xbcp.ptn.azd-insightsdashboardavm-ptn-azd-insightsdashboard-module-owners-bicep
30avm/ptn/azd/ml-ai-environment46d3xbcp.ptn.azd-mlaienvironmentavm-ptn-azd-mlaienvironment-module-owners-bicep
31avm/ptn/azd/ml-hub-dependencies46d3xbcp.ptn.azd-mlhubdependenciesavm-ptn-azd-mlhubdependencies-module-owners-bicep
32avm/ptn/azd/ml-project46d3xbcp.ptn.azd-mlprojectavm-ptn-azd-mlproject-module-owners-bicep
33avm/ptn/azd/monitoring46d3xbcp.ptn.azd-monitoringavm-ptn-azd-monitoring-module-owners-bicep
34avm/ptn/data/private-analytical-workspace46d3xbcp.ptn.data-privateanalyticalworkspaceavm-ptn-data-privateanalyticalworkspace-module-owners-bicep
35avm/ptn/deployment-script/create-kv-ssh-key-pair46d3xbcp.ptn.deploymentscript-createkvsshkeypairavm-ptn-deploymentscript-createkvsshkeypair-module-owners-bicep
36avm/ptn/deployment-script/import-image-to-acr46d3xbcp.ptn.deploymentscript-importimagetoacravm-ptn-deploymentscript-importimagetoacr-module-owners-bicep
37avm/ptn/deployment-script/private46d3xbcp.ptn.deploymentscript-privateavm-ptn-deploymentscript-private-module-owners-bicep
38avm/ptn/dev-center/dev-box46d3xbcp.ptn.devcenter-devboxavm-ptn-devcenter-devbox-module-owners-bicep
39avm/ptn/dev-ops/cicd-agents-and-runners46d3xbcp.ptn.devops-cicdagentsandrunnersavm-ptn-devops-cicdagentsandrunners-module-owners-bicep
40avm/ptn/finops-toolkit/finops-hub46d3xbcp.ptn.finopstoolkit-finopshubavm-ptn-finopstoolkit-finopshub-module-owners-bicep
41avm/ptn/lz/sub-vending46d3xbcp.ptn.lz-subvendingavm-ptn-lz-subvending-module-owners-bicep
42avm/ptn/lza-shared/data-services46d3xbcp.ptn.lzashared-dataservicesavm-ptn-lzashared-dataservices-module-owners-bicep
43avm/ptn/maintenance/azure-update-manager46d3xbcp.ptn.maintenance-azureupdatemanageravm-ptn-maintenance-azureupdatemanager-module-owners-bicep
44avm/ptn/mgmt-groups/subscription-placement46d3xbcp.ptn.mgmtgroup-subplacementavm-ptn-mgmtgroups-subscriptionplacement-module-owners-bicep
45avm/ptn/monitoring/amba46d3xbcp.ptn.monitoring-ambaavm-ptn-monitoring-amba-module-owners-bicep
46avm/ptn/monitoring/amba-alz46d3xbcp.ptn.monitoring-ambaalzavm-ptn-monitoring-ambaalz-module-owners-bicep
47avm/ptn/network/hub-networking46d3xbcp.ptn.network-hubnetworkingavm-ptn-network-hubnetworking-module-owners-bicep
48avm/ptn/network/private-link-private-dns-zones46d3xbcp.ptn.network-privatelinkprivatednszonesavm-ptn-network-privatelinkprivatednszones-module-owners-bicep
49avm/ptn/network/virtual-wan46d3xbcp.ptn.network-virtualwanavm-ptn-network-virtualwan-module-owners-bicep
50avm/ptn/network/vwan-connected-vnets46d3xbcp.ptn.network-vwanconnectedvnetsavm-ptn-network-vwanconnectedvnets-module-owners-bicep
51avm/ptn/openai/e2e-baseline46d3xbcp.ptn.openai-e2ebaselineavm-ptn-openai-e2ebaseline-module-owners-bicep
52avm/ptn/policy-insights/remediation46d3xbcp.ptn.policyinsights-remediationavm-ptn-policyinsights-remediation-module-owners-bicep
53avm/ptn/sa/build-your-own-copilot46d3xbcp.ptn.sa-buildyourowncopilotavm-ptn-sa-buildyourowncopilot-module-owners-bicep
54avm/ptn/sa/chat-with-your-data46d3xbcp.ptn.sa-chatwithyourdataavm-ptn-sa-chatwithyourdata-module-owners-bicep
55avm/ptn/sa/content-generation46d3xbcp.ptn.sa-contentgenerationavm-ptn-sa-contentgeneration-module-owners-bicep
56avm/ptn/sa/content-processing46d3xbcp.ptn.sa-contentprocessingavm-ptn-sa-contentprocessing-module-owners-bicep
57avm/ptn/sa/conversation-knowledge-mining46d3xbcp.ptn.sa-convknowledgeminingavm-ptn-sa-conversationknowledgemining-module-owners-bicep
58avm/ptn/sa/customer-chatbot46d3xbcp.ptn.sa-customerchatbotavm-ptn-sa-customerchatbot-module-owners-bicep
59avm/ptn/sa/document-knowledge-mining46d3xbcp.ptn.sa-documentknowledgeminingavm-ptn-sa-documentknowledgemining-module-owners-bicep
60avm/ptn/sa/modernize-your-code46d3xbcp.ptn.sa-modernizeyourcodeavm-ptn-sa-modernizeyourcode-module-owners-bicep
61avm/ptn/sa/multi-agent-custom-automation-engine46d3xbcp.ptn.sa-multiagentcustautengavm-ptn-sa-multiagentcustomautomationengine-module-owners-bicep
62avm/ptn/security/security-center46d3xbcp.ptn.security-securitycenteravm-ptn-security-securitycenter-module-owners-bicep
63avm/ptn/security/sentinel46d3xbcp.ptn.security-sentinelavm-ptn-security-sentinel-module-owners-bicep
64avm/ptn/subscription/service-health-alerts46d3xbcp.ptn.subscription-svchealthalertsavm-ptn-subscription-servicehealthalerts-module-owners-bicep
65avm/ptn/virtual-machine-images/azure-image-builder46d3xbcp.ptn.vmimages-azureimagebuilderavm-ptn-virtualmachineimages-azureimagebuilder-module-owners-bicep

Bicep Utility Modules

Module catalog

LanguageClassificationPublished 🟢 & 🟡Proposed ⚪SUM
BicepUtility112
➕ Additional information
Legend

Summary of status icons used on this page

IconStatusDescription
Proposed modulesModules that are proposed and/or being worked on but not published yet.
🟢 & 🟡Published modulesAvailable (🟢) and Orphaned (🟡) modules that are active and usable.
🔴Deprecated modulesModules that reached the end of their lifecycle.
📇All modulesIncluding Published, Proposed and Deprecated ones.

See the Module Lifecycle page for more details.

Info

This page contains various views of the module index (catalog) for Bicep Utility Modules. To see these views, click on the expandable sections with the “➕” sign below.

  • To see the full, unfiltered, unformatted module index on GitHub, click here.

  • To download the source CSV file, click here.

Note

Modules listed below that aren’t shown with the status of Module Available 🟢, are currently in development and are not yet available for use. For proposed modules, see the Proposed modules section below.

Published modules - 🟢 & 🟡

➕ Published Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/utl/types/avm-common-typesAVM Common TypesAlexanderSehr
Alexander Sehr

Proposed modules - ⚪

➕ Proposed Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/utl/general/get-environmentGet-Environmentalex-frankel
Alex Frankel

Deprecated modules - 🔴

➕ Deprecated Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01❌ None listed❌ None listed❌ None listed❌ None listed

All modules - 📇

➕ All Modules - Module names, status and owners
No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/utl/general/get-environmentGet-Environmentalex-frankel
Alex Frankel
02avm/utl/types/avm-common-typesAVM Common TypesAlexanderSehr
Alexander Sehr

Module Publication History - 📅

➕ Module Publication History - Module names, status and owners

Modules published in October 2024

No.Module NameDisplay NameStatus & VersionsOwner(s)
01avm/utl/types/avm-common-typesAVM Common TypesAlexanderSehr
Alexander Sehr

For Module Owners & Contributors

Note

This section is mainly intended for module owners and contributors as it contains information important for module development, such as telemetry ID prefix, and GitHub Teams for Owners.

Module name, Telemetry ID prefix, GitHub Teams for Owners

➕ All Modules - Module name, Telemetry ID prefix, GitHub Teams for Owners
No.Module NameTelemetry ID prefixGitHub Teams for Module Owners (@Azure org)
01avm/utl/general/get-environment46d3xbcp.utl.general-getenvironmentavm-utl-general-getenvironment-module-owners-bicep
02avm/utl/types/avm-common-types46d3xbcp.utl.types-avmcommontypesavm-utl-types-avmcommontypes-module-owners-bicep

Terraform Modules

Summary

The following table shows the number of all available, orphaned and planned Terraform Modules.

LanguageClassificationPublished 🟢 & 🟡Proposed ⚪SUM
TerraformResource11141152
Pattern263056
Utility11314
➕ Additional information
Legend

Summary of status icons used on this page

IconStatusDescription
Proposed modulesModules that are proposed and/or being worked on but not published yet.
🟢 & 🟡Published modulesAvailable (🟢) and Orphaned (🟡) modules that are active and usable.
🔴Deprecated modulesModules that reached the end of their lifecycle.
📇All modulesIncluding Published, Proposed and Deprecated ones.

See the Module Lifecycle page for more details.

Want to contribute to AVM Terraform modules?
#LabelsLink and description
1.Type: New Module Proposal 💡
Needs: Module Owner 📣
Language: Terraform 🌐
To become the owner of a new Terraform module, see all new Terraform modules looking for owners or check out the “Looking for owners” swimlane here.
2.Status: Module Orphaned 🟡
Language: Terraform 🌐
To become the owner of an orphaned Terraform module, see all orphaned Terraform modules or check out the “Orphaned” swimlane here.
3.Needs: Module Contributor 📣 Language: Terraform 🌐To become a co-owner or contribute to a Terraform module, see all Terraform modules looking for contributors.

For more details on “What are the different ways to contribute to AVM?”, see here.

Subsections of Terraform

Terraform Resource Modules

Module catalog

LanguageClassificationPublished 🟢 & 🟡Proposed ⚪SUM
TerraformResource11141152
➕ Additional information
Legend

Summary of status icons used on this page

IconStatusDescription
Proposed modulesModules that are proposed and/or being worked on but not published yet.
🟢 & 🟡Published modulesAvailable (🟢) and Orphaned (🟡) modules that are active and usable.
🔴Deprecated modulesModules that reached the end of their lifecycle.
📇All modulesIncluding Published, Proposed and Deprecated ones.

See the Module Lifecycle page for more details.

Info

This page contains various views of the module index (catalog) for Terraform Resource Modules. To see these views, click on the expandable sections with the “➕” sign below.

  • To see the full, unfiltered, unformatted module index on GitHub, click here.

  • To download the source CSV file, click here.

Note

Modules listed below that aren’t shown with the status of Module Available 🟢, are currently in development and are not yet available for use. For proposed modules, see the Proposed modules section below.

Published modules - 🟢 & 🟡

➕ Published Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01avm-res-apimanagement-service📄API Management Serviceswatilekhapaul
Swatilekha Paul
02avm-res-app-containerapp📄Container Applonegunmanb
Zijie He
03avm-res-app-job📄App Jobsujaypillai
Sujay Pillai
04avm-res-app-managedenvironment
📄App Managed Environmentsegraef
Sebastian Graef
05avm-res-appconfiguration-configurationstore
📄App Configuration Storematt-FFFFFF
Matt White
06avm-res-authorization-roleassignment📄Role Assignmentjaredfholgate
Jared Holgate
07avm-res-automation-automationaccount📄Automation Accountdidayal-msft
Divyadeep Dayal
Poven795909
Poornima Venkataramanan
08avm-res-avs-privatecloud
📄AVS Private Cloudjchancellor-ms
Jon Chancellor
09avm-res-azurestackhci-cluster📄Azure Stack HCI Clusterchirag1603
Chirag Choudha
10avm-res-azurestackhci-logicalnetwork📄AzureStackHCI logical networkchirag1603
Chirag Choudha
11avm-res-azurestackhci-virtualmachineinstance📄Stack HCI Virtual Machine Instancechirag1603
Chirag Choudha
12avm-res-batch-batchaccount📄Batch Accountethanjenkins1
Ethan Jenkins
13avm-res-botservice-botservice📄Bot Serviceethanjenkins1
Ethan Jenkins
14avm-res-cache-redis📄Redis Cachejchancellor-ms
Jon Chancellor
15avm-res-cache-redisenterprise📄Azure Managed RedisAnkur1106
Ankur Sharma
16avm-res-cdn-profile📄CDN ProfilePoven795909
Poornima Venkataramanan
didayal-msft
Divyadeep Dayal
17avm-res-certificateregistration-certificateorder📄Certificate Orderslonegunmanb
Zijie He
18avm-res-cognitiveservices-account📄Cognitive Servicelonegunmanb
Zijie He
19avm-res-communication-emailservice📄Email Communication Servicelonegunmanb
Zijie He
20avm-res-compute-capacityreservationgroup
📄Capacity Reservation GroupWenAI2020
Wen Tian
21avm-res-compute-disk📄Compute Diskterrymandin
Terry Mandin
22avm-res-compute-diskencryptionset📄Disk Encryption SetAkashc0807
Akash Choudhary
23avm-res-compute-gallery📄Azure Compute GalleryAkashc0807
Akash Choudhary
24avm-res-compute-hostgroup📄Host GroupsWenAI2020
Wen Tian
25avm-res-compute-proximityplacementgroup📄Proximity Placement Groupfafriha
Farouk Friha
26avm-res-compute-sshpublickey📄Public SSH KeyChrisSidebotham
Chris Sidebotham
27avm-res-compute-virtualmachine
📄Virtual Machine
VM
jchancellor-ms
Jon Chancellor
28avm-res-compute-virtualmachinescaleset
📄Virtual Machine Scale Set
VMSS
terrymandin
Terry Mandin
marcelkmfst
Marcel Keller
29avm-res-containerinstance-containergroup📄Container Instancesharmilamusunuru
Sharmila Musunuru
30avm-res-containerregistry-registry
📄Azure Container Registry (ACR)Akashc0807
Akash Choudhary
31avm-res-containerservice-managedcluster
📄AKS Managed Clustermatt-FFFFFF
Matt White
ethanjenkins1
Ethan Jenkins
32avm-res-databricks-workspace📄Azure Databricks Workspacesegraef
Sebastian Graef
33avm-res-datafactory-factory📄Data Factoryasishr
Asish R
34avm-res-dataprotection-backupvault📄Data Protection Backup Vaultethanjenkins1
Ethan Jenkins
35avm-res-dataprotection-resourceguard📄Data Protection Resource GuardWenAI2020
Wen Tian
36avm-res-dbformysql-flexibleserver📄DB for MySQL Flexible Serverelsalvos
Cesar Abrego
37avm-res-dbforpostgresql-flexibleserver
📄DB for Postgre SQL Flexible Serverzaidmohd
Zaid Mohammad
38avm-res-desktopvirtualization-applicationgroup📄Azure Virtual Desktop (AVD) Application Groupjensheerin
Jen Sheerin
sihbher
Gerardo Reyes
39avm-res-desktopvirtualization-hostpool📄Azure Virtual Desktop (AVD) Host Pooljensheerin
Jen Sheerin
sihbher
Gerardo Reyes
40avm-res-desktopvirtualization-scalingplan📄Azure Virtual Desktop (AVD) Scaling Planjensheerin
Jen Sheerin
sihbher
Gerardo Reyes
41avm-res-desktopvirtualization-workspace📄Azure Virtual Desktop (AVD) Workspacejensheerin
Jen Sheerin
sihbher
Gerardo Reyes
42avm-res-devcenter-devcenter
📄Dev Centerabhishekaryams
Abhishek Arya
43avm-res-devopsinfrastructure-pool📄DevOps Poolsjaredfholgate
Jared Holgate
44avm-res-documentdb-databaseaccount📄CosmosDB Database Accountcmaneu
Christopher Maneu
45avm-res-documentdb-mongocluster📄Cosmos DB for MongoDB (vCore)sujaypillai
Sujay Pillai
46avm-res-edge-site📄Azure Arc Site managerchirag1603
Chirag Choudha
47avm-res-eventgrid-domain
📄Event Grid Domainsujaypillai
Sujay Pillai
48avm-res-eventgrid-namespace
📄Event Grid Namespacedassbernd
Berna Sandalli
49avm-res-eventgrid-topic
📄Event Grid Topicsujaypillai
Sujay Pillai
50avm-res-eventhub-namespace📄Event Hub Namespacercavaturu
Raja Kalyan Ram Cavaturu
51avm-res-features-feature📄Azure Feature Exposure Control (AFEC)lonegunmanb
Zijie He
52avm-res-hybridcontainerservice-provisionedclusterinstance📄AKS Arcchirag1603
Chirag Choudha
53avm-res-insights-autoscalesetting
📄Auto scale settingsMinHeinA
Min Hein Aung
54avm-res-insights-component📄Application InsightJfolberth
John Folberth
55avm-res-insights-datacollectionendpoint📄Data Collection Endpointsharmilamusunuru
Sharmila Musunuru
56avm-res-insights-datacollectionrule📄Data Collection Ruleethanjenkins1
Ethan Jenkins
57avm-res-keyvault-vault
📄Key Vault
KV
matt-FFFFFF
Matt White
58avm-res-kusto-cluster
📄Kusto ClustersLaurentLesle
Laurent Lesle
59avm-res-logic-workflow📄Logic Apps (Workflow)bakrish
Bala Krishnamoorthy
60avm-res-machinelearningservices-workspace📄Machine Learning Services Workspace
ML Workspace
Nepomuceno
Gabriel Monteiro Nepomuceno
61avm-res-maintenance-maintenanceconfiguration📄Maintenance ConfigurationASHR4
Rhys Ash
62avm-res-managedidentity-userassignedidentity📄User Assigned Identity
MSI
Jfolberth
John Folberth
63avm-res-management-servicegroup
📄Management Service Groupshaflidif
Haflidi Fridthjofsson
64avm-res-netapp-netappaccount
📄Azure NetApp Filejtracey93
Jack Tracey
65avm-res-network-applicationgateway📄Application Gateway
App GW
mofaizal
Mohamed Faizal
66avm-res-network-applicationgatewaywebapplicationfirewallpolicy📄Application Gateway Web Application Firewall (WAF) Policymofaizal
Mohamed Faizal
67avm-res-network-applicationsecuritygroup📄Application Security Group (ASG)
ASG
MinHeinA
Min Hein Aung
68avm-res-network-azurefirewall📄Azure Firewall
Azure FW
vmisson
Vincent Misson
69avm-res-network-bastionhost📄Bastion Hostomer5574
Omer Sharon
70avm-res-network-connection📄Virtual Network Gateway Connectionjchancellor-ms
Jon Chancellor
71avm-res-network-ddosprotectionplan📄DDoS Protectionsitarant
Simona Tarantola
jtracey93
Jack Tracey
72avm-res-network-dnsresolver
📄DNS Resolveromer5574
Omer Sharon
73avm-res-network-dnszone📄Public DNS Zonesharmilamusunuru
Sharmila Musunuru
74avm-res-network-expressroutecircuit📄ExpressRoute Circuit
ER
khushal08
Khush Kaviraj
adammontlake
Adam Montlake
75avm-res-network-firewallpolicy
📄Azure Firewall PolicyMinHeinA
Min Hein Aung
76avm-res-network-frontdoorwebapplicationfirewallpolicy📄Front Door Web Application Firewall (WAF) Policysihbher
Gerardo Reyes
77avm-res-network-ipgroup📄IP Groupmathewsg
Mathews George
78avm-res-network-loadbalancer📄Loadbalancerdonovm4
Donovan McCoy
79avm-res-network-localnetworkgateway📄Local Network GatewayBhavyasree08
Bhavyasree Damarla
80avm-res-network-natgateway📄NAT Gatewayiarik0440
Yarik Simineans
81avm-res-network-networkinterface📄Network Interface
NIC
fafriha
Farouk Friha
82avm-res-network-networkmanager
📄Azure Virtual Network ManagerMrRoundRobin
Robin Muller
83avm-res-network-networksecuritygroup📄Network Security Groupmaheshbenke
Mahesh Benke
84avm-res-network-networkwatcher📄Azure Network Watcherterrymandin
Terry Mandin
85avm-res-network-privatednszone
📄Private DNS ZoneMinHeinA
Min Hein Aung
86avm-res-network-privateendpoint📄Private Endpointjaredfholgate
Jared Holgate
87avm-res-network-publicipaddress📄Public IP Address
PIP
vmisson
Vincent Misson
88avm-res-network-publicipprefix📄Public IP PrefixPmeshramPM
Pankaj Meshram
89avm-res-network-routetable
📄Route Table
UDR
adammontlake
Adam Montlake
90avm-res-network-trafficmanagerprofile
📄Traffic Manager ProfileAnubhaR94
Anubha Rana
91avm-res-network-virtualnetwork
📄Virtual Network
VNET
jaredfholgate
Jared Holgate
92avm-res-operationalinsights-workspace
📄Log Analytics workspaceiarik0440
Yarik Simineans
93avm-res-oracledatabase-cloudexadatainfrastructure📄Oracle Exadata Infrastructuresihbher
Gerardo Reyes
terrymandin
Terry Mandin
94avm-res-oracledatabase-cloudvmcluster📄Oracle VM clustersihbher
Gerardo Reyes
terrymandin
Terry Mandin
95avm-res-portal-dashboard📄Azure Portal DashboardVeronicaSea
Veronica Xu
96avm-res-recoveryservices-vault
📄Recovery Services Vaultelsalvos
Cesar Abrego
97avm-res-redhatopenshift-openshiftcluster📄OpenShift Clusterethanjenkins1
Ethan Jenkins
puneetdevadiga
Puneet Devadiga
98avm-res-relay-namespace
📄Relay Namespacesujaypillai
Sujay Pillai
99avm-res-resourcegraph-query📄Resource Graph QuerySJAYAP
S Jayaprakash
100avm-res-resources-resourcegroup📄Resource Group
RG
Jfolberth
John Folberth
101avm-res-search-searchservice📄Search Servicelestermarch
Lester March
102avm-res-servicebus-namespace📄Service Bus Namespaceiarik0440
Yarik Simineans
103avm-res-sql-managedinstance📄SQL Managed Instance
SQL MI
chanakanissanka
Chanaka Nissanka
abhishekaryams
Abhishek Arya
104avm-res-sql-server
📄Azure SQL Serverabhishekaryams
Abhishek Arya
105avm-res-sqlvirtualmachine-sqlvirtualmachine📄Sql Virtual Machine
SQL VM
sujaypillai
Sujay Pillai
106avm-res-storage-storageaccount📄Storage Accountchinthakaru
Chinthaka Rupasinghe
107avm-res-web-connection📄API Connectiondonovm4
Donovan McCoy
108avm-res-web-hostingenvironment
📄App Service Environment
ASE
matt-FFFFFF
Matt White
109avm-res-web-serverfarm📄App Service Planmatt-FFFFFF
Matt White
ethanjenkins1
Ethan Jenkins
110avm-res-web-site
📄Web/Function App
App Service, Web Site, Logic App, Function App
donovm4
Donovan McCoy
111avm-res-web-staticsite📄Static Web Appdonovm4
Donovan McCoy

Proposed modules - ⚪

➕ Proposed Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01avm-res-aad-domainservicen/aAzure Active Directory Domain Servicehumanascode
Itamar Hirosh
02avm-res-alertsmanagement-actionrulen/aAction Rulesxsatishx
Satish Balakrishnan
03avm-res-analysisservices-servern/aAnalysis Services ServerBhavyasree08
Bhavyasree Damarla
04avm-res-compute-imagen/aImage
05avm-res-consumption-budgetn/aConsumption BudgetAkashc0807
Akash Choudhary
06avm-res-containerservice-fleetn/aAKS Fleetdidayal-msft
Divyadeep Dayal
amruta53
Amruta Kulkarni
07avm-res-dashboard-grafanan/aAzure Managed Grafanakhajour
Abdelaziz Khajour
08avm-res-databricks-accessconnectorn/aAzure Databricks Access Connector
09avm-res-deviceregistry-assetendpointprofilen/aDevice Registry Asset Endpoint Profilelonegunmanb
Zijie He
10avm-res-devtestlab-labn/aDevTest Labsharmilamusunuru
Sharmila Musunuru
11avm-res-digitaltwins-digitaltwinsinstancen/aDigital Twins Instance
12avm-res-eventgrid-systemtopicn/aEvent Grid System Topicsujaypillai
Sujay Pillai
13avm-res-healthbot-healthbotn/aAzure Health Bot
14avm-res-hybridcompute-machinen/aHybrid Compute Machine
15avm-res-insights-actiongroupn/aAction Grouparjenhuitema
Arjen Huitema
Brunoga-MS
Bruno Gabrielli
16avm-res-insights-activitylogalertn/aActivity log alertstagolovina
Tanya Golovina
17avm-res-insights-logprofilen/aLog profilesharmilamusunuru
Sharmila Musunuru
18avm-res-insights-metricalertn/aMetric Alertarjenhuitema
Arjen Huitema
19avm-res-insights-privatelinkscopen/aAzure Monitor Private Link Scopesharmilamusunuru
Sharmila Musunuru
20avm-res-insights-scheduledqueryrulen/aScheduled Query Rulexsatishx
Satish Balakrishnan
21avm-res-iotoperations-instancen/aAzure IoT Operationsagreaves-ms
Allen Greaves
WilliamBerryiii
Bill Berry
22avm-res-loadtestservice-loadtestn/aLoad Testing Servicepfayika1
Philippe Fayika
23avm-res-managedservices-registrationdefinitionn/aRegistration Definition (Lighthouse)
24avm-res-management-managementgroupn/aManagement Group
MG
herms14
Hermes Miraflor II
25avm-res-network-dnsforwardingrulesetn/aDNS Forwarding Rulesetsharmilamusunuru
Sharmila Musunuru
26avm-res-network-expressrouteportn/aExpressRoute Portadammontlake
Adam Montlake
27avm-res-network-networksecurityperimetern/aNetwork Security Perimetersujaypillai
Sujay Pillai
28avm-res-network-privatelinkservicen/aPrivate Link Serviceavivshrem
Aviv Shrem
29avm-res-network-serviceendpointpolicyn/aService Endpoint PolicyASHR4
Rhys Ash
30avm-res-network-virtualnetworkgatewayn/aVirtual Network Gateway
VNET GW

31avm-res-network-virtualroutern/aRoute Server
32avm-res-operationsmanagement-solutionn/aOperations Management Solution
33avm-res-powerbidedicated-capacityn/aPower BI Dedicated Capacity
34avm-res-purview-accountn/aPurview Account
35avm-res-resources-featuren/aResource Featureslonegunmanb
Zijie He
36avm-res-servicefabric-clustern/aService Fabric Cluster
37avm-res-servicenetworking-trafficcontrollern/aApplication Gateway for Containers (Traffic Controller)mofaizal
Mohamed Faizal
38avm-res-signalrservice-signalrn/aSignalR Service SignalR
39avm-res-sql-instancepooln/aInstance Poolssujaypillai
Sujay Pillai
40avm-res-synapse-workspacen/aSynapse WorkspaceKarni-G
Karni Gupta
41avm-res-virtualmachineimages-imagetemplaten/aVirtual Machine Image Templatetravishankins
Travis Hankins

Deprecated modules - 🔴

➕ Deprecated Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01❌ None listed❌ None listed❌ None listed❌ None listed❌ None listed

All modules - 📇

➕ All Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01avm-res-aad-domainservicen/aAzure Active Directory Domain Servicehumanascode
Itamar Hirosh
02avm-res-alertsmanagement-actionrulen/aAction Rulesxsatishx
Satish Balakrishnan
03avm-res-analysisservices-servern/aAnalysis Services ServerBhavyasree08
Bhavyasree Damarla
04avm-res-apimanagement-service📄API Management Serviceswatilekhapaul
Swatilekha Paul
05avm-res-app-containerapp📄Container Applonegunmanb
Zijie He
06avm-res-app-job📄App Jobsujaypillai
Sujay Pillai
07avm-res-app-managedenvironment
📄App Managed Environmentsegraef
Sebastian Graef
08avm-res-appconfiguration-configurationstore
📄App Configuration Storematt-FFFFFF
Matt White
09avm-res-authorization-roleassignment📄Role Assignmentjaredfholgate
Jared Holgate
10avm-res-automation-automationaccount📄Automation Accountdidayal-msft
Divyadeep Dayal
Poven795909
Poornima Venkataramanan
11avm-res-avs-privatecloud
📄AVS Private Cloudjchancellor-ms
Jon Chancellor
12avm-res-azurestackhci-cluster📄Azure Stack HCI Clusterchirag1603
Chirag Choudha
13avm-res-azurestackhci-logicalnetwork📄AzureStackHCI logical networkchirag1603
Chirag Choudha
14avm-res-azurestackhci-virtualmachineinstance📄Stack HCI Virtual Machine Instancechirag1603
Chirag Choudha
15avm-res-batch-batchaccount📄Batch Accountethanjenkins1
Ethan Jenkins
16avm-res-botservice-botservice📄Bot Serviceethanjenkins1
Ethan Jenkins
17avm-res-cache-redis📄Redis Cachejchancellor-ms
Jon Chancellor
18avm-res-cache-redisenterprise📄Azure Managed RedisAnkur1106
Ankur Sharma
19avm-res-cdn-profile📄CDN ProfilePoven795909
Poornima Venkataramanan
didayal-msft
Divyadeep Dayal
20avm-res-certificateregistration-certificateorder📄Certificate Orderslonegunmanb
Zijie He
21avm-res-cognitiveservices-account📄Cognitive Servicelonegunmanb
Zijie He
22avm-res-communication-emailservice📄Email Communication Servicelonegunmanb
Zijie He
23avm-res-compute-capacityreservationgroup
📄Capacity Reservation GroupWenAI2020
Wen Tian
24avm-res-compute-disk📄Compute Diskterrymandin
Terry Mandin
25avm-res-compute-diskencryptionset📄Disk Encryption SetAkashc0807
Akash Choudhary
26avm-res-compute-gallery📄Azure Compute GalleryAkashc0807
Akash Choudhary
27avm-res-compute-hostgroup📄Host GroupsWenAI2020
Wen Tian
28avm-res-compute-imagen/aImage
29avm-res-compute-proximityplacementgroup📄Proximity Placement Groupfafriha
Farouk Friha
30avm-res-compute-sshpublickey📄Public SSH KeyChrisSidebotham
Chris Sidebotham
31avm-res-compute-virtualmachine
📄Virtual Machine
VM
jchancellor-ms
Jon Chancellor
32avm-res-compute-virtualmachinescaleset
📄Virtual Machine Scale Set
VMSS
terrymandin
Terry Mandin
marcelkmfst
Marcel Keller
33avm-res-consumption-budgetn/aConsumption BudgetAkashc0807
Akash Choudhary
34avm-res-containerinstance-containergroup📄Container Instancesharmilamusunuru
Sharmila Musunuru
35avm-res-containerregistry-registry
📄Azure Container Registry (ACR)Akashc0807
Akash Choudhary
36avm-res-containerservice-fleetn/aAKS Fleetdidayal-msft
Divyadeep Dayal
amruta53
Amruta Kulkarni
37avm-res-containerservice-managedcluster
📄AKS Managed Clustermatt-FFFFFF
Matt White
ethanjenkins1
Ethan Jenkins
38avm-res-dashboard-grafanan/aAzure Managed Grafanakhajour
Abdelaziz Khajour
39avm-res-databricks-accessconnectorn/aAzure Databricks Access Connector
40avm-res-databricks-workspace📄Azure Databricks Workspacesegraef
Sebastian Graef
41avm-res-datafactory-factory📄Data Factoryasishr
Asish R
42avm-res-dataprotection-backupvault📄Data Protection Backup Vaultethanjenkins1
Ethan Jenkins
43avm-res-dataprotection-resourceguard📄Data Protection Resource GuardWenAI2020
Wen Tian
44avm-res-dbformysql-flexibleserver📄DB for MySQL Flexible Serverelsalvos
Cesar Abrego
45avm-res-dbforpostgresql-flexibleserver
📄DB for Postgre SQL Flexible Serverzaidmohd
Zaid Mohammad
46avm-res-desktopvirtualization-applicationgroup📄Azure Virtual Desktop (AVD) Application Groupjensheerin
Jen Sheerin
sihbher
Gerardo Reyes
47avm-res-desktopvirtualization-hostpool📄Azure Virtual Desktop (AVD) Host Pooljensheerin
Jen Sheerin
sihbher
Gerardo Reyes
48avm-res-desktopvirtualization-scalingplan📄Azure Virtual Desktop (AVD) Scaling Planjensheerin
Jen Sheerin
sihbher
Gerardo Reyes
49avm-res-desktopvirtualization-workspace📄Azure Virtual Desktop (AVD) Workspacejensheerin
Jen Sheerin
sihbher
Gerardo Reyes
50avm-res-devcenter-devcenter
📄Dev Centerabhishekaryams
Abhishek Arya
51avm-res-deviceregistry-assetendpointprofilen/aDevice Registry Asset Endpoint Profilelonegunmanb
Zijie He
52avm-res-devopsinfrastructure-pool📄DevOps Poolsjaredfholgate
Jared Holgate
53avm-res-devtestlab-labn/aDevTest Labsharmilamusunuru
Sharmila Musunuru
54avm-res-digitaltwins-digitaltwinsinstancen/aDigital Twins Instance
55avm-res-documentdb-databaseaccount📄CosmosDB Database Accountcmaneu
Christopher Maneu
56avm-res-documentdb-mongocluster📄Cosmos DB for MongoDB (vCore)sujaypillai
Sujay Pillai
57avm-res-edge-site📄Azure Arc Site managerchirag1603
Chirag Choudha
58avm-res-eventgrid-domain
📄Event Grid Domainsujaypillai
Sujay Pillai
59avm-res-eventgrid-namespace
📄Event Grid Namespacedassbernd
Berna Sandalli
60avm-res-eventgrid-systemtopicn/aEvent Grid System Topicsujaypillai
Sujay Pillai
61avm-res-eventgrid-topic
📄Event Grid Topicsujaypillai
Sujay Pillai
62avm-res-eventhub-namespace📄Event Hub Namespacercavaturu
Raja Kalyan Ram Cavaturu
63avm-res-features-feature📄Azure Feature Exposure Control (AFEC)lonegunmanb
Zijie He
64avm-res-healthbot-healthbotn/aAzure Health Bot
65avm-res-hybridcompute-machinen/aHybrid Compute Machine
66avm-res-hybridcontainerservice-provisionedclusterinstance📄AKS Arcchirag1603
Chirag Choudha
67avm-res-insights-actiongroupn/aAction Grouparjenhuitema
Arjen Huitema
Brunoga-MS
Bruno Gabrielli
68avm-res-insights-activitylogalertn/aActivity log alertstagolovina
Tanya Golovina
69avm-res-insights-autoscalesetting
📄Auto scale settingsMinHeinA
Min Hein Aung
70avm-res-insights-component📄Application InsightJfolberth
John Folberth
71avm-res-insights-datacollectionendpoint📄Data Collection Endpointsharmilamusunuru
Sharmila Musunuru
72avm-res-insights-datacollectionrule📄Data Collection Ruleethanjenkins1
Ethan Jenkins
73avm-res-insights-logprofilen/aLog profilesharmilamusunuru
Sharmila Musunuru
74avm-res-insights-metricalertn/aMetric Alertarjenhuitema
Arjen Huitema
75avm-res-insights-privatelinkscopen/aAzure Monitor Private Link Scopesharmilamusunuru
Sharmila Musunuru
76avm-res-insights-scheduledqueryrulen/aScheduled Query Rulexsatishx
Satish Balakrishnan
77avm-res-iotoperations-instancen/aAzure IoT Operationsagreaves-ms
Allen Greaves
WilliamBerryiii
Bill Berry
78avm-res-keyvault-vault
📄Key Vault
KV
matt-FFFFFF
Matt White
79avm-res-kusto-cluster
📄Kusto ClustersLaurentLesle
Laurent Lesle
80avm-res-loadtestservice-loadtestn/aLoad Testing Servicepfayika1
Philippe Fayika
81avm-res-logic-workflow📄Logic Apps (Workflow)bakrish
Bala Krishnamoorthy
82avm-res-machinelearningservices-workspace📄Machine Learning Services Workspace
ML Workspace
Nepomuceno
Gabriel Monteiro Nepomuceno
83avm-res-maintenance-maintenanceconfiguration📄Maintenance ConfigurationASHR4
Rhys Ash
84avm-res-managedidentity-userassignedidentity📄User Assigned Identity
MSI
Jfolberth
John Folberth
85avm-res-managedservices-registrationdefinitionn/aRegistration Definition (Lighthouse)
86avm-res-management-managementgroupn/aManagement Group
MG
herms14
Hermes Miraflor II
87avm-res-management-servicegroup
📄Management Service Groupshaflidif
Haflidi Fridthjofsson
88avm-res-netapp-netappaccount
📄Azure NetApp Filejtracey93
Jack Tracey
89avm-res-network-applicationgateway📄Application Gateway
App GW
mofaizal
Mohamed Faizal
90avm-res-network-applicationgatewaywebapplicationfirewallpolicy📄Application Gateway Web Application Firewall (WAF) Policymofaizal
Mohamed Faizal
91avm-res-network-applicationsecuritygroup📄Application Security Group (ASG)
ASG
MinHeinA
Min Hein Aung
92avm-res-network-azurefirewall📄Azure Firewall
Azure FW
vmisson
Vincent Misson
93avm-res-network-bastionhost📄Bastion Hostomer5574
Omer Sharon
94avm-res-network-connection📄Virtual Network Gateway Connectionjchancellor-ms
Jon Chancellor
95avm-res-network-ddosprotectionplan📄DDoS Protectionsitarant
Simona Tarantola
jtracey93
Jack Tracey
96avm-res-network-dnsforwardingrulesetn/aDNS Forwarding Rulesetsharmilamusunuru
Sharmila Musunuru
97avm-res-network-dnsresolver
📄DNS Resolveromer5574
Omer Sharon
98avm-res-network-dnszone📄Public DNS Zonesharmilamusunuru
Sharmila Musunuru
99avm-res-network-expressroutecircuit📄ExpressRoute Circuit
ER
khushal08
Khush Kaviraj
adammontlake
Adam Montlake
100avm-res-network-expressrouteportn/aExpressRoute Portadammontlake
Adam Montlake
101avm-res-network-firewallpolicy
📄Azure Firewall PolicyMinHeinA
Min Hein Aung
102avm-res-network-frontdoorwebapplicationfirewallpolicy📄Front Door Web Application Firewall (WAF) Policysihbher
Gerardo Reyes
103avm-res-network-ipgroup📄IP Groupmathewsg
Mathews George
104avm-res-network-loadbalancer📄Loadbalancerdonovm4
Donovan McCoy
105avm-res-network-localnetworkgateway📄Local Network GatewayBhavyasree08
Bhavyasree Damarla
106avm-res-network-natgateway📄NAT Gatewayiarik0440
Yarik Simineans
107avm-res-network-networkinterface📄Network Interface
NIC
fafriha
Farouk Friha
108avm-res-network-networkmanager
📄Azure Virtual Network ManagerMrRoundRobin
Robin Muller
109avm-res-network-networksecuritygroup📄Network Security Groupmaheshbenke
Mahesh Benke
110avm-res-network-networksecurityperimetern/aNetwork Security Perimetersujaypillai
Sujay Pillai
111avm-res-network-networkwatcher📄Azure Network Watcherterrymandin
Terry Mandin
112avm-res-network-privatednszone
📄Private DNS ZoneMinHeinA
Min Hein Aung
113avm-res-network-privateendpoint📄Private Endpointjaredfholgate
Jared Holgate
114avm-res-network-privatelinkservicen/aPrivate Link Serviceavivshrem
Aviv Shrem
115avm-res-network-publicipaddress📄Public IP Address
PIP
vmisson
Vincent Misson
116avm-res-network-publicipprefix📄Public IP PrefixPmeshramPM
Pankaj Meshram
117avm-res-network-routetable
📄Route Table
UDR
adammontlake
Adam Montlake
118avm-res-network-serviceendpointpolicyn/aService Endpoint PolicyASHR4
Rhys Ash
119avm-res-network-trafficmanagerprofile
📄Traffic Manager ProfileAnubhaR94
Anubha Rana
120avm-res-network-virtualnetwork
📄Virtual Network
VNET
jaredfholgate
Jared Holgate
121avm-res-network-virtualnetworkgatewayn/aVirtual Network Gateway
VNET GW

122avm-res-network-virtualroutern/aRoute Server
123avm-res-operationalinsights-workspace
📄Log Analytics workspaceiarik0440
Yarik Simineans
124avm-res-operationsmanagement-solutionn/aOperations Management Solution
125avm-res-oracledatabase-cloudexadatainfrastructure📄Oracle Exadata Infrastructuresihbher
Gerardo Reyes
terrymandin
Terry Mandin
126avm-res-oracledatabase-cloudvmcluster📄Oracle VM clustersihbher
Gerardo Reyes
terrymandin
Terry Mandin
127avm-res-portal-dashboard📄Azure Portal DashboardVeronicaSea
Veronica Xu
128avm-res-powerbidedicated-capacityn/aPower BI Dedicated Capacity
129avm-res-purview-accountn/aPurview Account
130avm-res-recoveryservices-vault
📄Recovery Services Vaultelsalvos
Cesar Abrego
131avm-res-redhatopenshift-openshiftcluster📄OpenShift Clusterethanjenkins1
Ethan Jenkins
puneetdevadiga
Puneet Devadiga
132avm-res-relay-namespace
📄Relay Namespacesujaypillai
Sujay Pillai
133avm-res-resourcegraph-query📄Resource Graph QuerySJAYAP
S Jayaprakash
134avm-res-resources-featuren/aResource Featureslonegunmanb
Zijie He
135avm-res-resources-resourcegroup📄Resource Group
RG
Jfolberth
John Folberth
136avm-res-search-searchservice📄Search Servicelestermarch
Lester March
137avm-res-servicebus-namespace📄Service Bus Namespaceiarik0440
Yarik Simineans
138avm-res-servicefabric-clustern/aService Fabric Cluster
139avm-res-servicenetworking-trafficcontrollern/aApplication Gateway for Containers (Traffic Controller)mofaizal
Mohamed Faizal
140avm-res-signalrservice-signalrn/aSignalR Service SignalR
141avm-res-sql-instancepooln/aInstance Poolssujaypillai
Sujay Pillai
142avm-res-sql-managedinstance📄SQL Managed Instance
SQL MI
chanakanissanka
Chanaka Nissanka
abhishekaryams
Abhishek Arya
143avm-res-sql-server
📄Azure SQL Serverabhishekaryams
Abhishek Arya
144avm-res-sqlvirtualmachine-sqlvirtualmachine📄Sql Virtual Machine
SQL VM
sujaypillai
Sujay Pillai
145avm-res-storage-storageaccount📄Storage Accountchinthakaru
Chinthaka Rupasinghe
146avm-res-synapse-workspacen/aSynapse WorkspaceKarni-G
Karni Gupta
147avm-res-virtualmachineimages-imagetemplaten/aVirtual Machine Image Templatetravishankins
Travis Hankins
148avm-res-web-connection📄API Connectiondonovm4
Donovan McCoy
149avm-res-web-hostingenvironment
📄App Service Environment
ASE
matt-FFFFFF
Matt White
150avm-res-web-serverfarm📄App Service Planmatt-FFFFFF
Matt White
ethanjenkins1
Ethan Jenkins
151avm-res-web-site
📄Web/Function App
App Service, Web Site, Logic App, Function App
donovm4
Donovan McCoy
152avm-res-web-staticsite📄Static Web Appdonovm4
Donovan McCoy

Module Publication History - 📅

➕ Module Publication History - Module names, status and owners

Modules published in April 2026

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-insights-datacollectionrule📄Data Collection Ruleethanjenkins1
Ethan Jenkins

Modules published in March 2026

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-botservice-botservice📄Bot Serviceethanjenkins1
Ethan Jenkins

Modules published in February 2026

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-cache-redisenterprise📄Azure Managed RedisAnkur1106
Ankur Sharma
02avm-res-eventgrid-namespace📄Event Grid Namespacedassbernd
Berna Sandalli
03avm-res-network-trafficmanagerprofile📄Traffic Manager ProfileAnubhaR94
Anubha Rana
04avm-res-sqlvirtualmachine-sqlvirtualmachine📄Sql Virtual Machine
SQL VM
sujaypillai
Sujay Pillai

Modules published in January 2026

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-eventgrid-domain📄Event Grid Domainsujaypillai
Sujay Pillai
02avm-res-eventgrid-topic📄Event Grid Topicsujaypillai
Sujay Pillai
03avm-res-relay-namespace📄Relay Namespacesujaypillai
Sujay Pillai

Modules published in November 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-features-feature📄Azure Feature Exposure Control (AFEC)lonegunmanb
Zijie He
02avm-res-web-connection📄API Connectiondonovm4
Donovan McCoy

Modules published in October 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-management-servicegroup📄Management Service Groupshaflidif
Haflidi Fridthjofsson
02avm-res-redhatopenshift-openshiftcluster📄OpenShift Clusterethanjenkins1
Ethan Jenkins
puneetdevadiga
Puneet Devadiga

Modules published in August 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-compute-capacityreservationgroup📄Capacity Reservation GroupWenAI2020
Wen Tian
02avm-res-documentdb-mongocluster📄Cosmos DB for MongoDB (vCore)sujaypillai
Sujay Pillai

Modules published in July 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-certificateregistration-certificateorder📄Certificate Orderslonegunmanb
Zijie He
02avm-res-dataprotection-resourceguard📄Data Protection Resource GuardWenAI2020
Wen Tian

Modules published in June 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-communication-emailservice📄Email Communication Servicelonegunmanb
Zijie He

Modules published in May 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-app-job📄App Jobsujaypillai
Sujay Pillai
02avm-res-batch-batchaccount📄Batch Accountethanjenkins1
Ethan Jenkins
03avm-res-dataprotection-backupvault📄Data Protection Backup Vaultethanjenkins1
Ethan Jenkins

Modules published in April 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-apimanagement-service📄API Management Serviceswatilekhapaul
Swatilekha Paul
02avm-res-automation-automationaccount📄Automation Accountdidayal-msft
Divyadeep Dayal
Poven795909
Poornima Venkataramanan
03avm-res-datafactory-factory📄Data Factoryasishr
Asish R
04avm-res-eventhub-namespace📄Event Hub Namespacercavaturu
Raja Kalyan Ram Cavaturu
05avm-res-maintenance-maintenanceconfiguration📄Maintenance ConfigurationASHR4
Rhys Ash
06avm-res-network-ipgroup📄IP Groupmathewsg
Mathews George
07avm-res-network-publicipprefix📄Public IP PrefixPmeshramPM
Pankaj Meshram
08avm-res-resourcegraph-query📄Resource Graph QuerySJAYAP
S Jayaprakash

Modules published in March 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-appconfiguration-configurationstore📄App Configuration Storematt-FFFFFF
Matt White

Modules published in February 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-network-applicationgatewaywebapplicationfirewallpolicy📄Application Gateway Web Application Firewall (WAF) Policymofaizal
Mohamed Faizal

Modules published in January 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-netapp-netappaccount📄Azure NetApp Filejtracey93
Jack Tracey
02avm-res-network-applicationsecuritygroup📄Application Security Group (ASG)
ASG
MinHeinA
Min Hein Aung
03avm-res-network-connection📄Virtual Network Gateway Connectionjchancellor-ms
Jon Chancellor
04avm-res-recoveryservices-vault📄Recovery Services Vaultelsalvos
Cesar Abrego

Modules published in December 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-compute-gallery📄Azure Compute GalleryAkashc0807
Akash Choudhary

Modules published in November 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-compute-proximityplacementgroup📄Proximity Placement Groupfafriha
Farouk Friha
02avm-res-containerservice-managedcluster📄AKS Managed Clustermatt-FFFFFF
Matt White
ethanjenkins1
Ethan Jenkins
03avm-res-insights-autoscalesetting📄Auto scale settingsMinHeinA
Min Hein Aung
04avm-res-network-localnetworkgateway📄Local Network GatewayBhavyasree08
Bhavyasree Damarla
05avm-res-network-networkinterface📄Network Interface
NIC
fafriha
Farouk Friha

Modules published in October 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-compute-diskencryptionset📄Disk Encryption SetAkashc0807
Akash Choudhary
02avm-res-devcenter-devcenter📄Dev Centerabhishekaryams
Abhishek Arya
03avm-res-network-expressroutecircuit📄ExpressRoute Circuit
ER
khushal08
Khush Kaviraj
adammontlake
Adam Montlake
04avm-res-network-frontdoorwebapplicationfirewallpolicy📄Front Door Web Application Firewall (WAF) Policysihbher
Gerardo Reyes

Modules published in September 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-dbformysql-flexibleserver📄DB for MySQL Flexible Serverelsalvos
Cesar Abrego
02avm-res-dbforpostgresql-flexibleserver📄DB for Postgre SQL Flexible Serverzaidmohd
Zaid Mohammad
03avm-res-network-privateendpoint📄Private Endpointjaredfholgate
Jared Holgate
04avm-res-oracledatabase-cloudexadatainfrastructure📄Oracle Exadata Infrastructuresihbher
Gerardo Reyes
terrymandin
Terry Mandin
05avm-res-oracledatabase-cloudvmcluster📄Oracle VM clustersihbher
Gerardo Reyes
terrymandin
Terry Mandin
06avm-res-portal-dashboard📄Azure Portal DashboardVeronicaSea
Veronica Xu
07avm-res-sql-managedinstance📄SQL Managed Instance
SQL MI
chanakanissanka
Chanaka Nissanka
abhishekaryams
Abhishek Arya
08avm-res-sql-server📄Azure SQL Serverabhishekaryams
Abhishek Arya

Modules published in August 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-azurestackhci-cluster📄Azure Stack HCI Clusterchirag1603
Chirag Choudha
02avm-res-azurestackhci-logicalnetwork📄AzureStackHCI logical networkchirag1603
Chirag Choudha
03avm-res-azurestackhci-virtualmachineinstance📄Stack HCI Virtual Machine Instancechirag1603
Chirag Choudha
04avm-res-compute-hostgroup📄Host GroupsWenAI2020
Wen Tian
05avm-res-devopsinfrastructure-pool📄DevOps Poolsjaredfholgate
Jared Holgate
06avm-res-documentdb-databaseaccount📄CosmosDB Database Accountcmaneu
Christopher Maneu
07avm-res-edge-site📄Azure Arc Site managerchirag1603
Chirag Choudha
08avm-res-hybridcontainerservice-provisionedclusterinstance📄AKS Arcchirag1603
Chirag Choudha
09avm-res-insights-component📄Application InsightJfolberth
John Folberth
10avm-res-insights-datacollectionendpoint📄Data Collection Endpointsharmilamusunuru
Sharmila Musunuru
11avm-res-network-applicationgateway📄Application Gateway
App GW
mofaizal
Mohamed Faizal
12avm-res-network-dnszone📄Public DNS Zonesharmilamusunuru
Sharmila Musunuru
13avm-res-resources-resourcegroup📄Resource Group
RG
Jfolberth
John Folberth
14avm-res-search-searchservice📄Search Servicelestermarch
Lester March
15avm-res-web-serverfarm📄App Service Planmatt-FFFFFF
Matt White
ethanjenkins1
Ethan Jenkins

Modules published in July 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-app-containerapp📄Container Applonegunmanb
Zijie He
02avm-res-containerinstance-containergroup📄Container Instancesharmilamusunuru
Sharmila Musunuru
03avm-res-machinelearningservices-workspace📄Machine Learning Services Workspace
ML Workspace
Nepomuceno
Gabriel Monteiro Nepomuceno
04avm-res-network-networkwatcher📄Azure Network Watcherterrymandin
Terry Mandin

Modules published in June 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-cache-redis📄Redis Cachejchancellor-ms
Jon Chancellor
02avm-res-logic-workflow📄Logic Apps (Workflow)bakrish
Bala Krishnamoorthy
03avm-res-web-hostingenvironment📄App Service Environment
ASE
matt-FFFFFF
Matt White

Modules published in May 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-cdn-profile📄CDN ProfilePoven795909
Poornima Venkataramanan
didayal-msft
Divyadeep Dayal
02avm-res-compute-disk📄Compute Diskterrymandin
Terry Mandin
03avm-res-compute-sshpublickey📄Public SSH KeyChrisSidebotham
Chris Sidebotham
04avm-res-network-dnsresolver📄DNS Resolveromer5574
Omer Sharon
05avm-res-network-routetable📄Route Table
UDR
adammontlake
Adam Montlake

Modules published in April 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-kusto-cluster📄Kusto ClustersLaurentLesle
Laurent Lesle
02avm-res-servicebus-namespace📄Service Bus Namespaceiarik0440
Yarik Simineans

Modules published in March 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-databricks-workspace📄Azure Databricks Workspacesegraef
Sebastian Graef
02avm-res-managedidentity-userassignedidentity📄User Assigned Identity
MSI
Jfolberth
John Folberth
03avm-res-network-privatednszone📄Private DNS ZoneMinHeinA
Min Hein Aung

Modules published in February 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-app-managedenvironment📄App Managed Environmentsegraef
Sebastian Graef
02avm-res-avs-privatecloud📄AVS Private Cloudjchancellor-ms
Jon Chancellor
03avm-res-cognitiveservices-account📄Cognitive Servicelonegunmanb
Zijie He
04avm-res-compute-virtualmachinescaleset📄Virtual Machine Scale Set
VMSS
terrymandin
Terry Mandin
marcelkmfst
Marcel Keller
05avm-res-containerregistry-registry📄Azure Container Registry (ACR)Akashc0807
Akash Choudhary
06avm-res-network-bastionhost📄Bastion Hostomer5574
Omer Sharon
07avm-res-network-networksecuritygroup📄Network Security Groupmaheshbenke
Mahesh Benke
08avm-res-network-publicipaddress📄Public IP Address
PIP
vmisson
Vincent Misson
09avm-res-storage-storageaccount📄Storage Accountchinthakaru
Chinthaka Rupasinghe
10avm-res-web-site📄Web/Function App
App Service, Web Site, Logic App, Function App
donovm4
Donovan McCoy
11avm-res-web-staticsite📄Static Web Appdonovm4
Donovan McCoy

Modules published in January 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-authorization-roleassignment📄Role Assignmentjaredfholgate
Jared Holgate
02avm-res-network-azurefirewall📄Azure Firewall
Azure FW
vmisson
Vincent Misson
03avm-res-network-firewallpolicy📄Azure Firewall PolicyMinHeinA
Min Hein Aung
04avm-res-network-networkmanager📄Azure Virtual Network ManagerMrRoundRobin
Robin Muller
05avm-res-operationalinsights-workspace📄Log Analytics workspaceiarik0440
Yarik Simineans

Modules published in December 2023

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-desktopvirtualization-applicationgroup📄Azure Virtual Desktop (AVD) Application Groupjensheerin
Jen Sheerin
sihbher
Gerardo Reyes
02avm-res-desktopvirtualization-scalingplan📄Azure Virtual Desktop (AVD) Scaling Planjensheerin
Jen Sheerin
sihbher
Gerardo Reyes
03avm-res-desktopvirtualization-workspace📄Azure Virtual Desktop (AVD) Workspacejensheerin
Jen Sheerin
sihbher
Gerardo Reyes
04avm-res-network-natgateway📄NAT Gatewayiarik0440
Yarik Simineans

Modules published in November 2023

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-compute-virtualmachine📄Virtual Machine
VM
jchancellor-ms
Jon Chancellor
02avm-res-network-ddosprotectionplan📄DDoS Protectionsitarant
Simona Tarantola
jtracey93
Jack Tracey
03avm-res-network-loadbalancer📄Loadbalancerdonovm4
Donovan McCoy

Modules published in October 2023

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-desktopvirtualization-hostpool📄Azure Virtual Desktop (AVD) Host Pooljensheerin
Jen Sheerin
sihbher
Gerardo Reyes
02avm-res-network-virtualnetwork📄Virtual Network
VNET
jaredfholgate
Jared Holgate

Modules published in September 2023

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-res-keyvault-vault📄Key Vault
KV
matt-FFFFFF
Matt White

Terraform Pattern Modules

Module catalog

LanguageClassificationPublished 🟢 & 🟡Proposed ⚪SUM
TerraformPattern263056
➕ Additional information
Legend

Summary of status icons used on this page

IconStatusDescription
Proposed modulesModules that are proposed and/or being worked on but not published yet.
🟢 & 🟡Published modulesAvailable (🟢) and Orphaned (🟡) modules that are active and usable.
🔴Deprecated modulesModules that reached the end of their lifecycle.
📇All modulesIncluding Published, Proposed and Deprecated ones.

See the Module Lifecycle page for more details.

Info

This page contains various views of the module index (catalog) for Terraform Pattern Modules. To see these views, click on the expandable sections with the “➕” sign below.

  • To see the full, unfiltered, unformatted module index on GitHub, click here.

  • To download the source CSV file, click here.

Note

Modules listed below that aren’t shown with the status of Module Available 🟢, are currently in development and are not yet available for use. For proposed modules, see the Proposed modules section below.

Published modules - 🟢 & 🟡

➕ Published Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01avm-ptn-aca-lza-hosting-environment
📄Azure Container Apps Landing Zone Acceleratorsam-cogan
Sam Cogan
02avm-ptn-aiml-ai-foundry
📄AI-ML - AI Foundrysegraef
Sebastian Graef
mbilalamjad
Bilal Amjad
03avm-ptn-aiml-landing-zone
📄AI-ML - Landing Zone (LZ)jchancellor-ms
Jon Chancellor
mbilalamjad
Bilal Amjad
04avm-ptn-aks-dev📄AKS devms-henglu
Heng Lu
05avm-ptn-aks-economy📄AKS economyms-henglu
Heng Lu
06avm-ptn-aks-enterprise📄AKS Enterprisems-henglu
Heng Lu
07avm-ptn-aks-production📄Azure Kubernetes Servicechanakanissanka
Chanaka Nissanka
08avm-ptn-alz📄Azure Landing Zone Management Groups and Policy
ALZ Management Groups and Policy
matt-FFFFFF
Matt White
09avm-ptn-alz-connectivity-hub-and-spoke-vnet
📄ALZ Connectivity Hub and Spoke
Azure Landing Zones - Hub and Spoke
jaredfholgate
Jared Holgate
10avm-ptn-alz-connectivity-virtual-wan
📄ALZ Connectivity vWAN
Azure Landing Zones - vWAN
jaredfholgate
Jared Holgate
11avm-ptn-alz-management📄ALZ Management
Azure Landing Zones - Management
matt-FFFFFF
Matt White
12avm-ptn-alz-sub-vending
📄ALZ Subscription vending
Azure Landing Zones - Sub vending
matt-FFFFFF
Matt White
jaredfholgate
Jared Holgate
13avm-ptn-avd-lza-insights📄AVD Insights
Azure Virtual Desktop Insights
jensheerin
Jen Sheerin
sihbher
Gerardo Reyes
14avm-ptn-avd-lza-managementplane📄AVD Management Plane
Azure Virtual Desktop Management Plane
jensheerin
Jen Sheerin
sihbher
Gerardo Reyes
15avm-ptn-azuremonitorwindowsagent📄Azure Monitor Windows Agentchirag1603
Chirag Choudha
16avm-ptn-cicd-agents-and-runners
📄CI CD Agents and Runnersjaredfholgate
Jared Holgate
17avm-ptn-commercial-marketplace📄Commercial Marketplace SaaS Acceleratorethanjenkins1
Ethan Jenkins
18avm-ptn-ephemeral-credential📄Ephemeral Credentials Generatorlonegunmanb
Zijie He
19avm-ptn-function-app-storage-private-endpoints📄Function App and private endpoint-secured Storagedonovm4
Donovan McCoy
20avm-ptn-hci-ad-provisioner📄Arc for AD registrationchirag1603
Chirag Choudha
21avm-ptn-hci-server-provisioner📄Arc for Server registrationchirag1603
Chirag Choudha
22avm-ptn-monitoring-amba-alz📄AMBA ALZ Pattern
Azure Monitor Baseline Alerts - Azure Landing Zones Pattern
arjenhuitema
Arjen Huitema
Brunoga-MS
Bruno Gabrielli
23avm-ptn-network-private-link-private-dns-zones📄Private Link Private DNS Zonesjtracey93
Jack Tracey
24avm-ptn-network-routeserver📄Azure Route Serverjchancellor-ms
Jon Chancellor
25avm-ptn-odaa
📄Oracle Exedata Workloadterrymandin
Terry Mandin
26avm-ptn-policyassignment📄Policy assignmentbjornhofer
Bjorn Hofer

Proposed modules - ⚪

➕ Proposed Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01avm-ptn-ai-platform-baselinen/aAI platform baselineNepomuceno
Gabriel Monteiro Nepomuceno
02avm-ptn-aiml-ai-gatewayn/aAI-ML - Gatewaymbilalamjad
Bilal Amjad
03avm-ptn-alz-application-landing-zone-cicd-bootstrap-azure-devopsn/aALZ Application Landing Zone - CICD Bootstrap for Azure DevOpsjaredfholgate
Jared Holgate
04avm-ptn-alz-application-landing-zone-cicd-bootstrap-githubn/aALZ Application Landing Zone - CICD Bootstrap for GitHubjaredfholgate
Jared Holgate
05avm-ptn-alz-application-landing-zone-identity-and-accessn/aALZ Identity and Access Managementjaredfholgate
Jared Holgate
matt-FFFFFF
Matt White
06avm-ptn-alz-identityn/aALZ Identity
Azure Landing Zones - Identity

07avm-ptn-alz-policy-exemptionsn/aALZ Policy Exemptionsjaredfholgate
Jared Holgate
matt-FFFFFF
Matt White
08avm-ptn-app-iaas-vm-cosmosdb-tier-fourn/aWorkload - IaaS VM Cosmos DB - Tier 4mikestiers
Mike Stiers
andbron
Andrew Lambert
09avm-ptn-app-service-landing-zonen/aApp Service Landing Zonejaredfholgate
Jared Holgate
10avm-ptn-avd-lza-sessionhostsn/aAVD Session Hosts
Azure Virtual Desktop Session Hosts

11avm-ptn-azure-aws-s2s-vpnn/aAzure-AWS S2S VPNShironB
Shiron Babi
12avm-ptn-azure-ipamn/aIPAM
IP Address Management

13avm-ptn-azureimagebuildern/aAzure Image Builderethanjenkins1
Ethan Jenkins
14avm-ptn-bcdr-vm-replicationn/aAzure Site Recovery VM Replicationns-github-design
Nasreen Sarah
15avm-ptn-botservice-teamsappn/aBot Service Teams App
16avm-ptn-cicd-bootstrapn/aCI CD bootstrap
17avm-ptn-cloudshell-vnetn/aAzure Cloud Shell in a Virtual Networktravishankins
Travis Hankins
18avm-ptn-confidential-computen/aAzure Confidential Computeabhilashasr21
Abhilasha Srivastava
19avm-ptn-dev-center-dev-boxn/aMicrosoft Dev Center Dev Boxautocloudarc
Preston Parsard
20avm-ptn-lbvmssn/aVirtual Machine Scale Setterrymandin
Terry Mandin
21avm-ptn-mongodb-atlas-lzan/aMongoDB Atlas on Azure - Landing Zone (LZ)cloud-architect-dev
Deven Wagle
22avm-ptn-odaa-identityn/aOracle Identityterrymandin
Terry Mandin
kohei3110
Kohei Saito
23avm-ptn-openai-cognitivesearchn/aCorporate Line of Business (LoB) ChatBotmikestiers
Mike Stiers
24avm-ptn-openai-e2e-baselinen/aBaseline OpenAI end-to-end chat
25avm-ptn-oracle-iaasn/aOracle Database on Azuresihbher
Gerardo Reyes
26avm-ptn-pipeline-agent-container-jobn/aPipeline Agent with Azure Container Apps jobsmathewsg
Mathews George
27avm-ptn-purestorage-cbs-arrayn/aPure Storage Cloud Block Store on Azuresundarb19
Sundar Balaji Anantharamakrishnan
28avm-ptn-sentinel-solutionsn/aSentinel SolutionsLaurentLesle
Laurent Lesle
29avm-ptn-subnets-nsgs-routesn/aNetwork Security Groups
NSG
vamsi-boya
Vamsi Boya
30avm-ptn-subscription-service-health-alertsn/aSubscriptions Service Health AlertsASHR4
Rhys Ash

Deprecated modules - 🔴

➕ Deprecated Modules - Module names, status and owners

All modules - 📇

➕ All Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01avm-ptn-aca-lza-hosting-environment
📄Azure Container Apps Landing Zone Acceleratorsam-cogan
Sam Cogan
02avm-ptn-ai-platform-baselinen/aAI platform baselineNepomuceno
Gabriel Monteiro Nepomuceno
03avm-ptn-aiml-ai-foundry
📄AI-ML - AI Foundrysegraef
Sebastian Graef
mbilalamjad
Bilal Amjad
04avm-ptn-aiml-ai-gatewayn/aAI-ML - Gatewaymbilalamjad
Bilal Amjad
05avm-ptn-aiml-landing-zone
📄AI-ML - Landing Zone (LZ)jchancellor-ms
Jon Chancellor
mbilalamjad
Bilal Amjad
06avm-ptn-aks-dev📄AKS devms-henglu
Heng Lu
07avm-ptn-aks-economy📄AKS economyms-henglu
Heng Lu
08avm-ptn-aks-enterprise📄AKS Enterprisems-henglu
Heng Lu
09avm-ptn-aks-production📄Azure Kubernetes Servicechanakanissanka
Chanaka Nissanka
10avm-ptn-alz📄Azure Landing Zone Management Groups and Policy
ALZ Management Groups and Policy
matt-FFFFFF
Matt White
11avm-ptn-alz-application-landing-zone-cicd-bootstrap-azure-devopsn/aALZ Application Landing Zone - CICD Bootstrap for Azure DevOpsjaredfholgate
Jared Holgate
12avm-ptn-alz-application-landing-zone-cicd-bootstrap-githubn/aALZ Application Landing Zone - CICD Bootstrap for GitHubjaredfholgate
Jared Holgate
13avm-ptn-alz-application-landing-zone-identity-and-accessn/aALZ Identity and Access Managementjaredfholgate
Jared Holgate
matt-FFFFFF
Matt White
14avm-ptn-alz-connectivity-hub-and-spoke-vnet
📄ALZ Connectivity Hub and Spoke
Azure Landing Zones - Hub and Spoke
jaredfholgate
Jared Holgate
15avm-ptn-alz-connectivity-virtual-wan
📄ALZ Connectivity vWAN
Azure Landing Zones - vWAN
jaredfholgate
Jared Holgate
16avm-ptn-alz-identityn/aALZ Identity
Azure Landing Zones - Identity

17avm-ptn-alz-management📄ALZ Management
Azure Landing Zones - Management
matt-FFFFFF
Matt White
18avm-ptn-alz-policy-exemptionsn/aALZ Policy Exemptionsjaredfholgate
Jared Holgate
matt-FFFFFF
Matt White
19avm-ptn-alz-sub-vending
📄ALZ Subscription vending
Azure Landing Zones - Sub vending
matt-FFFFFF
Matt White
jaredfholgate
Jared Holgate
20avm-ptn-app-iaas-vm-cosmosdb-tier-fourn/aWorkload - IaaS VM Cosmos DB - Tier 4mikestiers
Mike Stiers
andbron
Andrew Lambert
21avm-ptn-app-service-landing-zonen/aApp Service Landing Zonejaredfholgate
Jared Holgate
22avm-ptn-avd-lza-insights📄AVD Insights
Azure Virtual Desktop Insights
jensheerin
Jen Sheerin
sihbher
Gerardo Reyes
23avm-ptn-avd-lza-managementplane📄AVD Management Plane
Azure Virtual Desktop Management Plane
jensheerin
Jen Sheerin
sihbher
Gerardo Reyes
24avm-ptn-avd-lza-sessionhostsn/aAVD Session Hosts
Azure Virtual Desktop Session Hosts

25avm-ptn-azure-aws-s2s-vpnn/aAzure-AWS S2S VPNShironB
Shiron Babi
26avm-ptn-azure-ipamn/aIPAM
IP Address Management

27avm-ptn-azureimagebuildern/aAzure Image Builderethanjenkins1
Ethan Jenkins
28avm-ptn-azuremonitorwindowsagent📄Azure Monitor Windows Agentchirag1603
Chirag Choudha
29avm-ptn-bcdr-vm-replicationn/aAzure Site Recovery VM Replicationns-github-design
Nasreen Sarah
30avm-ptn-botservice-teamsappn/aBot Service Teams App
31avm-ptn-cicd-agents-and-runners
📄CI CD Agents and Runnersjaredfholgate
Jared Holgate
32avm-ptn-cicd-bootstrapn/aCI CD bootstrap
33avm-ptn-cloudshell-vnetn/aAzure Cloud Shell in a Virtual Networktravishankins
Travis Hankins
34avm-ptn-commercial-marketplace📄Commercial Marketplace SaaS Acceleratorethanjenkins1
Ethan Jenkins
35avm-ptn-confidential-computen/aAzure Confidential Computeabhilashasr21
Abhilasha Srivastava
36avm-ptn-dev-center-dev-boxn/aMicrosoft Dev Center Dev Boxautocloudarc
Preston Parsard
37avm-ptn-ephemeral-credential📄Ephemeral Credentials Generatorlonegunmanb
Zijie He
38avm-ptn-function-app-storage-private-endpoints📄Function App and private endpoint-secured Storagedonovm4
Donovan McCoy
39avm-ptn-hci-ad-provisioner📄Arc for AD registrationchirag1603
Chirag Choudha
40avm-ptn-hci-server-provisioner📄Arc for Server registrationchirag1603
Chirag Choudha
41avm-ptn-hubnetworking📄Hub Networking
42avm-ptn-lbvmssn/aVirtual Machine Scale Setterrymandin
Terry Mandin
43avm-ptn-mongodb-atlas-lzan/aMongoDB Atlas on Azure - Landing Zone (LZ)cloud-architect-dev
Deven Wagle
44avm-ptn-monitoring-amba-alz📄AMBA ALZ Pattern
Azure Monitor Baseline Alerts - Azure Landing Zones Pattern
arjenhuitema
Arjen Huitema
Brunoga-MS
Bruno Gabrielli
45avm-ptn-network-private-link-private-dns-zones📄Private Link Private DNS Zonesjtracey93
Jack Tracey
46avm-ptn-network-routeserver📄Azure Route Serverjchancellor-ms
Jon Chancellor
47avm-ptn-odaa
📄Oracle Exedata Workloadterrymandin
Terry Mandin
48avm-ptn-odaa-identityn/aOracle Identityterrymandin
Terry Mandin
kohei3110
Kohei Saito
49avm-ptn-openai-cognitivesearchn/aCorporate Line of Business (LoB) ChatBotmikestiers
Mike Stiers
50avm-ptn-openai-e2e-baselinen/aBaseline OpenAI end-to-end chat
51avm-ptn-oracle-iaasn/aOracle Database on Azuresihbher
Gerardo Reyes
52avm-ptn-pipeline-agent-container-jobn/aPipeline Agent with Azure Container Apps jobsmathewsg
Mathews George
53avm-ptn-policyassignment📄Policy assignmentbjornhofer
Bjorn Hofer
54avm-ptn-purestorage-cbs-arrayn/aPure Storage Cloud Block Store on Azuresundarb19
Sundar Balaji Anantharamakrishnan
55avm-ptn-sentinel-solutionsn/aSentinel SolutionsLaurentLesle
Laurent Lesle
56avm-ptn-subnets-nsgs-routesn/aNetwork Security Groups
NSG
vamsi-boya
Vamsi Boya
57avm-ptn-subscription-service-health-alertsn/aSubscriptions Service Health AlertsASHR4
Rhys Ash
58avm-ptn-virtualwan
📄Virtual WAN
vWAN

59avm-ptn-vnetgateway📄Virtual Network Gateway
VNET GW


Module Publication History - 📅

➕ Module Publication History - Module names, status and owners

Modules published in April 2026

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-commercial-marketplace📄Commercial Marketplace SaaS Acceleratorethanjenkins1
Ethan Jenkins

Modules published in February 2026

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-aca-lza-hosting-environment📄Azure Container Apps Landing Zone Acceleratorsam-cogan
Sam Cogan

Modules published in January 2026

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-alz-sub-vending📄ALZ Subscription vending
Azure Landing Zones - Sub vending
matt-FFFFFF
Matt White
jaredfholgate
Jared Holgate

Modules published in November 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-ephemeral-credential📄Ephemeral Credentials Generatorlonegunmanb
Zijie He

Modules published in September 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-aiml-landing-zone📄AI-ML - Landing Zone (LZ)jchancellor-ms
Jon Chancellor
mbilalamjad
Bilal Amjad

Modules published in July 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-aiml-ai-foundry📄AI-ML - AI Foundrysegraef
Sebastian Graef
mbilalamjad
Bilal Amjad

Modules published in April 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-alz-connectivity-virtual-wan📄ALZ Connectivity vWAN
Azure Landing Zones - vWAN
jaredfholgate
Jared Holgate
02avm-ptn-function-app-storage-private-endpoints📄Function App and private endpoint-secured Storagedonovm4
Donovan McCoy
03avm-ptn-monitoring-amba-alz📄AMBA ALZ Pattern
Azure Monitor Baseline Alerts - Azure Landing Zones Pattern
arjenhuitema
Arjen Huitema
Brunoga-MS
Bruno Gabrielli

Modules published in March 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-alz-connectivity-hub-and-spoke-vnet📄ALZ Connectivity Hub and Spoke
Azure Landing Zones - Hub and Spoke
jaredfholgate
Jared Holgate

Modules published in November 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-aks-economy📄AKS economyms-henglu
Heng Lu
02avm-ptn-aks-enterprise📄AKS Enterprisems-henglu
Heng Lu

Modules published in October 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-aks-dev📄AKS devms-henglu
Heng Lu
02avm-ptn-odaa📄Oracle Exedata Workloadterrymandin
Terry Mandin
03avm-ptn-policyassignment📄Policy assignmentbjornhofer
Bjorn Hofer

Modules published in September 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-hubnetworkingn/aHub Networking

Modules published in August 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-azuremonitorwindowsagent📄Azure Monitor Windows Agentchirag1603
Chirag Choudha
02avm-ptn-cicd-agents-and-runners📄CI CD Agents and Runnersjaredfholgate
Jared Holgate
03avm-ptn-hci-ad-provisioner📄Arc for AD registrationchirag1603
Chirag Choudha
04avm-ptn-hci-server-provisioner📄Arc for Server registrationchirag1603
Chirag Choudha

Modules published in June 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-network-private-link-private-dns-zones📄Private Link Private DNS Zonesjtracey93
Jack Tracey
02avm-ptn-network-routeserver📄Azure Route Serverjchancellor-ms
Jon Chancellor

Modules published in May 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-avd-lza-insights📄AVD Insights
Azure Virtual Desktop Insights
jensheerin
Jen Sheerin
sihbher
Gerardo Reyes
02avm-ptn-avd-lza-managementplane📄AVD Management Plane
Azure Virtual Desktop Management Plane
jensheerin
Jen Sheerin
sihbher
Gerardo Reyes

Modules published in April 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-aks-production📄Azure Kubernetes Servicechanakanissanka
Chanaka Nissanka

Modules published in December 2023

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-alz-management📄ALZ Management
Azure Landing Zones - Management
matt-FFFFFF
Matt White

Modules published in November 2023

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-alz📄Azure Landing Zone Management Groups and Policy
ALZ Management Groups and Policy
matt-FFFFFF
Matt White
02avm-ptn-vnetgatewayn/aVirtual Network Gateway
VNET GW


Modules published in October 2023

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-ptn-virtualwann/aVirtual WAN
vWAN


Terraform Utility Modules

Module catalog

LanguageClassificationPublished 🟢 & 🟡Proposed ⚪SUM
TerraformUtility11314
➕ Additional information
Legend

Summary of status icons used on this page

IconStatusDescription
Proposed modulesModules that are proposed and/or being worked on but not published yet.
🟢 & 🟡Published modulesAvailable (🟢) and Orphaned (🟡) modules that are active and usable.
🔴Deprecated modulesModules that reached the end of their lifecycle.
📇All modulesIncluding Published, Proposed and Deprecated ones.

See the Module Lifecycle page for more details.

Info

This page contains various views of the module index (catalog) for Terraform Utility Modules. To see these views, click on the expandable sections with the “➕” sign below.

  • To see the full, unfiltered, unformatted module index on GitHub, click here.

  • To download the source CSV file, click here.

Note

Modules listed below that aren’t shown with the status of Module Available 🟢, are currently in development and are not yet available for use. For proposed modules, see the Proposed modules section below.

Published modules - 🟢 & 🟡

➕ Published Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01avm-utl-compute-linuxvirtualmachine-azapi-replicator📄Linux VM AzRM to AzAPI Replicatorlonegunmanb
Zijie He
02avm-utl-compute-orchestratedvirtualmachinescaleset-azapi-replicator
📄VMSS AzRM to AzAPI Replicatorlonegunmanb
Zijie He
03avm-utl-compute-windowsvirtualmachine-azapi-replicator📄Windows VM AzRM to AzAPI Replicatorlonegunmanb
Zijie He
04avm-utl-interfaces📄AVM Interfacesmatt-FFFFFF
Matt White
05avm-utl-network-ip-addresses📄AVM Network IP Addresses
IPv4 CIDR
jaredfholgate
Jared Holgate
06avm-utl-network-virtualnetwork-azapi-replicator📄Virtual Network AzRM to AzAPI Replicatorlonegunmanb
Zijie He
07avm-utl-privatedns-privatednszone-azapi-replicator📄Private DNS Zone AzRM to AzAPI Replicatorlonegunmanb
Zijie He
08avm-utl-regions
📄Azure Regions Datamatt-FFFFFF
Matt White
09avm-utl-resources-resourcegroup-azapi-replicator📄Resource Group AzRM to AzAPI Replicatorlonegunmanb
Zijie He
10avm-utl-roledefinitions
📄Azure Role Definitionsmatt-FFFFFF
Matt White
11avm-utl-sku-finder📄AVM SKU Finder
Sku
jchancellor-ms
Jon Chancellor

Proposed modules - ⚪

➕ Proposed Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01avm-utl-containerregistry-containerregistry-azapi-replicatorn/aContainer Registry AzRM to AzAPI Replicatorlonegunmanb
Zijie He
02avm-utl-namingn/aModule NamingNepomuceno
Gabriel Monteiro Nepomuceno
matt-FFFFFF
Matt White
03avm-utl-network-subnet-azapi-replicatorn/aSubnet AzRM to AzAPI Replicatorlonegunmanb
Zijie He

Deprecated modules - 🔴

➕ Deprecated Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01❌ None listed❌ None listed❌ None listed❌ None listed❌ None listed

All modules - 📇

➕ All Modules - Module names, status and owners
No.Module NameSource
Code
Display NameStatus & VersionsPrimary Owner
01avm-utl-compute-linuxvirtualmachine-azapi-replicator📄Linux VM AzRM to AzAPI Replicatorlonegunmanb
Zijie He
02avm-utl-compute-orchestratedvirtualmachinescaleset-azapi-replicator
📄VMSS AzRM to AzAPI Replicatorlonegunmanb
Zijie He
03avm-utl-compute-windowsvirtualmachine-azapi-replicator📄Windows VM AzRM to AzAPI Replicatorlonegunmanb
Zijie He
04avm-utl-containerregistry-containerregistry-azapi-replicatorn/aContainer Registry AzRM to AzAPI Replicatorlonegunmanb
Zijie He
05avm-utl-interfaces📄AVM Interfacesmatt-FFFFFF
Matt White
06avm-utl-namingn/aModule NamingNepomuceno
Gabriel Monteiro Nepomuceno
matt-FFFFFF
Matt White
07avm-utl-network-ip-addresses📄AVM Network IP Addresses
IPv4 CIDR
jaredfholgate
Jared Holgate
08avm-utl-network-subnet-azapi-replicatorn/aSubnet AzRM to AzAPI Replicatorlonegunmanb
Zijie He
09avm-utl-network-virtualnetwork-azapi-replicator📄Virtual Network AzRM to AzAPI Replicatorlonegunmanb
Zijie He
10avm-utl-privatedns-privatednszone-azapi-replicator📄Private DNS Zone AzRM to AzAPI Replicatorlonegunmanb
Zijie He
11avm-utl-regions
📄Azure Regions Datamatt-FFFFFF
Matt White
12avm-utl-resources-resourcegroup-azapi-replicator📄Resource Group AzRM to AzAPI Replicatorlonegunmanb
Zijie He
13avm-utl-roledefinitions
📄Azure Role Definitionsmatt-FFFFFF
Matt White
14avm-utl-sku-finder📄AVM SKU Finder
Sku
jchancellor-ms
Jon Chancellor

Module Publication History - 📅

➕ Module Publication History - Module names, status and owners

Modules published in January 2026

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-utl-compute-linuxvirtualmachine-azapi-replicator📄Linux VM AzRM to AzAPI Replicatorlonegunmanb
Zijie He
02avm-utl-compute-orchestratedvirtualmachinescaleset-azapi-replicator📄VMSS AzRM to AzAPI Replicatorlonegunmanb
Zijie He
03avm-utl-compute-windowsvirtualmachine-azapi-replicator📄Windows VM AzRM to AzAPI Replicatorlonegunmanb
Zijie He
04avm-utl-network-virtualnetwork-azapi-replicator📄Virtual Network AzRM to AzAPI Replicatorlonegunmanb
Zijie He
05avm-utl-privatedns-privatednszone-azapi-replicator📄Private DNS Zone AzRM to AzAPI Replicatorlonegunmanb
Zijie He
06avm-utl-resources-resourcegroup-azapi-replicator📄Resource Group AzRM to AzAPI Replicatorlonegunmanb
Zijie He

Modules published in September 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-utl-roledefinitions📄Azure Role Definitionsmatt-FFFFFF
Matt White

Modules published in March 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-utl-network-ip-addresses📄AVM Network IP Addresses
IPv4 CIDR
jaredfholgate
Jared Holgate

Modules published in January 2025

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-utl-interfaces📄AVM Interfacesmatt-FFFFFF
Matt White

Modules published in December 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-utl-sku-finder📄AVM SKU Finder
Sku
jchancellor-ms
Jon Chancellor

Modules published in August 2024

No.Module NameSource
Code
Display NameStatus & VersionsOwner(s)
01avm-utl-regions📄Azure Regions Datamatt-FFFFFF
Matt White

Subsections of Usage Guide

Concepts

Note

This page is a work in progress and will be updated as we improve & finalize the content. Please check back regularly for updates.

When developing an Azure solution using AVM modules, there are several aspects to consider. This page covers important concepts and provides guidance the technical decisions. Each concept/topic referenced here will be further detailed in the corresponding Bicep or Terraform specific guidance.

Language-agnostic concepts

Topics/concepts that are relevant and applicable for both Bicep and Terraform.

Module Sourcing

Public Registry

Leveraging the public registries (i.e., the Bicep Public Registry or the Terraform Public Registry) is the most common and recommended approach.

This allows you to leverage the latest and greatest features of the AVM modules, as well as the latest security updates. While there aren’t any prerequisites for using the public registry - no extra software component or service needs to be installed and no configuration is needed - the client machine the deployment is initiated from will need to have access to the public registry.

Private Registry (synced)

A private registry - that is hosted in your own environment - can store modules originating from the public registry. Using a private registry still grants you the latest version of AVM modules while allowing you to review each version of each module before admitting them to your private registry. You also have control over who can access your own private registry. Note that using a private registry means that you’re still using each module as is, without making any changes.

Inner-sourcing

Inner-sourcing AVM means maintaining your own, synchronized copy of AVM modules in your own internal private registry, repositories or other storage option. Customers normally look to inner-source AVM modules when they have strict security and compliance requirements, or when they want to publish their own lightly wrapped versions of the modules to meet their specific needs; for example changing some allowed or default values for parameter or variable inputs.

This is a more complex approach and requires more effort to maintain, but it can be beneficial in certain scenarios, however, it should not be the default approach as it can lead to a lot of overhead and maintenance and requires significant skills and resources to set up and maintain.

There are many ways to approach inner-sourcing AVM modules for both Bicep and Terraform. The AVM team will be publishing guidance on this topic, based on customer experience and learnings.

Tip

You can see the AVM team talking about inner-sourcing on the AVM February 2025 community call on YouTube.

Solution Development

This section provides advanced guidance for developing solutions using Azure Verified Modules (AVM). It covers technical decisions and concepts that are important for building and deploying Azure solutions using AVM modules.

Planning your solution

When implementing infrastructure in Azure leveraging IaaS and PaaS services, there are multiple options for Azure deployments. In this article we assume that a decision has been made to implement your solution, using Infrastructure-as-Code (IaC). This is best suited to allow programmatic declarative control of the target infrastructure and is ideal for projects that require repeatability and idempotency.

Choosing an Infrastructure-as-Code language

There are multiple language choices when implementing your solution using IaC in Azure. The Azure Verified Modules project currently supports Bicep and Terraform. The following guidance summarizes considerations that can help choose the option that best suits your requirements.

Reasons to choose Bicep

Bicep is the Microsoft 1st party offering for IaC deployments. It supports Generally Available (GA) and preview features for all Azure resources and allows for modular composition of resources and solution templates. The use of simplified syntax makes IaC development intuitive and the use of the Bicep extension for VSCode provides IntelliSense and syntax validation to assist with coding. Finally, Bicep is well suited for infrastructure projects and teams that don’t require management of other cloud platforms or services outside of Azure. For a more detailed read on reasons to choose Bicep, read this article from the Bicep documentation.

Reasons to choose Terraform

HashiCorp’s Terraform is an extensible 3rd party platform that can be used across multiple cloud and on-premises platforms using multiple provider plugins. It has widespread adoption due to its simplified human-readable configuration files, common functionality, and the ability to allow a project to span multiple provider spaces.

In Azure, support is provided through two primary providers called AzureRM and AzAPI respectively. The default provider for many Azure use cases is AzureRM which is co-developed between Microsoft and HashiCorp. It includes support for generally available (GA) features, while support for new and preview features might be slightly delayed following their initial release. AzAPI is developed exclusively by Microsoft and supports all preview and GA features while being more complex to use due to the more direct interaction with Azure’s APIs. While it is possible to use both providers in a single project as needed, the best practice is to standardize on a single provider as much as is reasonable.

Projects typically choose Terraform when they bridge multiple cloud infrastructure platforms or when the development team has previous experience coding in Terraform. Modern Integrated Development Environments (IDE) - such as Visual Studio Code - include extension support for Terraform features as well as additional Azure specific extensions. These extensions enable syntax validation and highlighting as well as code formatting and HashiCorp Cloud Platform (HCP) integration for HashiCorp Cloud customers. For a more detailed read on reasons to choose Terraform, read this article from the Terraform on Azure documentation.

Architecture design

Before starting the process of codifying infrastructure, it is important to develop a detailed architecture of what will be created. This should include details for:

  1. Organizational elements such as management groups, subscriptions, and resource groups as well as any tagging and Role Based Access (RBAC) configurations for each.
  2. Infrastructure services that will be created along with key configuration details like sku values, network CIDR range sizes, or other solution specific configuration.
  3. Any relationship between services that will be codified as part of the deployment.
  4. Identify inputs to your solution for designs that are intended to be used as templates.
Note

For a production grade solution, you need to

  • follow the recommendations of the Cloud Adoption Framework (CAF) and have your platform and application landing zones defined, as per Azure Landing Zones (ALZ);
  • follow the recommendations of the Azure Well-Architected Framework (WAF) to ensure that your solution is compliant with and integrates into your organization’s policies and standards. This includes considerations for security, identity, networking, monitoring, cost management, and governance.

Sourcing content for deployment

Once the architecture is agreed upon, it is time to plan the development of your IaC code. There are several key decision points that should be considered during this phase.

Content creation methods

The two primary methods used to create your solutions module are:

  1. Using base resources (“vanilla resources”) from scratch or
  2. Leveraging pre-created modules from the AVM library to minimize the time to value during development.

The trade-off between the two options is primarily around control vs. speed. AVM works to provide the best of both options by providing modules with opinionated and recommended practice defaults while allowing for more detailed configuration as needed. In our sample exercise we’ll be using AVM modules to demonstrate building the example solution.

AVM module type considerations

When using AVM modules for your solution, there is an additional choice that should be considered. The AVM library includes both pattern and resource module types. If your architecture includes or follows a well-known pattern then a pattern module may be the right option for you. If you determine this is the case, then search the module index for pattern modules in your chosen language to see if an option exists for your scenario. Otherwise, using resource modules from the library will be your best option.

In cases where an AVM resource or pattern module isn’t available for use, review the Bicep or Terraform provider documentation to identify how to augment AVM modules with standalone resources. If you feel that additional resource or pattern modules would be useful, you can also request the creation of a pattern or resource module by creating a module proposal issue on the AVM github repository.

Module source considerations

Once the decision has been made to use AVM modules to help accelerate solution development, a decision about where those modules will be sourced from is the next key decision point. A detailed exploration of the different sourcing options can be found in the Module Sourcing section of the Concepts page. Take a moment to review the options discussed there.

For our solution we will leverage the Public Registry option by sourcing AVM modules directly from the respective Terraform and Bicep public registries. This will avoid the need to fork copies of the modules for private use.

Subsections of Solution Development

Bicep - Solution Development

Introduction

Azure Verified Modules (AVM) for Bicep are a powerful tool that leverage the Bicep domain-specific language (DSL), industry knowledge, and an Open Source community, which altogether enable developers to quickly deploy Azure resources that follow Microsoft’s recommended practices for Azure.
In this article, we will walk through the Bicep specific considerations and recommended practices on developing your solution leveraging Azure Verified Modules. We’ll review some of the design features and trade-offs and include sample code to illustrate each discussion point.

In this tutorial, we will:

  • Deploy a basic Virtual Machine architecture into Azure
  • Explore recommended practices related to Bicep template development
  • Demonstrate the ease with which you can deploy AVM modules
  • Describe each of the development and deployment steps in detail

After completing this tutorial, you’ll have a working knowledge of:

  • How to discover and add AVM modules to your Bicep template
  • How to reference and use outputs across AVM modules
  • Recommended practices for parameterization and structure of your Bicep file
  • Configuration of AVM modules to meet Microsoft’s Well Architected Framework (WAF) principles
  • How to deploy your Bicep template into an Azure subscription from your local machine

Let’s get started!

Prerequisites

You will need the following tools and components to complete this guide:

Before you begin, make sure you have these tools installed in your development environment.

Solution Architecture

Before we begin coding, it is important to have details about what the infrastructure architecture will include. For our example, we will be building a solution that will host a simple application on a Linux virtual machine (VM). The solution must be secure and auditable. The VM must not be accessible from the internet and its logs should be easily accessible. All Azure services should utilize logging tools for auditing purposes.

Azure VM Solution Architecture

Develop the Solution Code

Creating the main.bicep file

The architecture diagram shows all components needed for a successful solution deployment. Rather than building the complete solution at once, this tutorial takes an incremental approach building the Bicep file piece-by-piece and testing the deployment at each stage. This approach allows for discussion of each design decision along the way.

The development will start with core platform components: first the backend logging services (Log Analytics) and then the virtual network.

Let’s begin by creating our folder structure along with a main.bicep file. Your folder structure should be as follows:

VirtualMachineAVM_Example1/
└── main.bicep

After you have your folder structure and main.bicep file, we can proceed with our first AVM resources!

Log Analytics

Let’s start by adding a logging service to our main.bicep since all other deployed resources will use this service for their logs.

Tip

Always begin template development by adding resources that create dependencies for other downstream services. This approach simplifies referencing these dependencies within your other modules as you develop them. For example, starting with Logging and Virtual Network services makes sense since all other services will depend on these.

The logging solution depicted in our Architecture Diagram shows we will be using a Log Analytics workspace. Let’s add that to our template! Open your main.bicep file and add the following:

➕ Expand Code
1module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
2  name: 'logAnalyticsWorkspace'
3  params: {
4    // Required parameters
5    name: 'VM-AVM-Ex1-law'
6    // Non-required parameters
7    location: 'westus2'
8  }
9}
Note

Always click on the “Copy to clipboard” button in the top right corner of the Code sample area in order not to have the line numbers included in the copied code.

You now have a fully functional Bicep template that will deploy a working Log Analytics workspace! If you would like to try it, run the following in your console:

Note

For keeping the example below simple, we are using the traditional deployment commands, e.g., az deployment group create or New-AzResourceGroupDeployment. However, we encourage you to look into using Deployment Stacks instead by simply replacing the previous commands with az stack group create or New-AzResourceGroupDeploymentStack as well as the other required input parameters as shown here.

Deployment Stacks allow you to deploy a Bicep file as a stack, which is a collection of resources that are deployed together. This allows you to manage the lifecycle of the stack as a single unit, making it easier to deploy, update, and now even delete resources via Bicep. You can also implement RBAC Deny Assignments on your stacks deployed resources to prevent changes to the resources or specific actions on the resources to all but an excluded list of users, groups or other principals.

Deploy with
# Log in to Azure
Connect-AzAccount

# Select your subscription
Set-AzContext -SubscriptionId '<subscriptionId>'

# Deploy a resource group
New-AzResourceGroup -Name 'avm-bicep-vmexample1' -Location '<location>'

# Invoke your deployment
New-AzResourceGroupDeployment -DeploymentName 'avm-bicep-vmexample1-deployment' -ResourceGroupName 'avm-bicep-vmexample1' -TemplateFile '/<path-to>/VirtualMachineAVM_Example1/main.bicep'
# Log in to Azure
az login

# Select your subscription
az account set --subscription '<subscriptionId>'

# Deploy a resource group
az group create --name 'avm-bicep-vmexample1' --location '<location>'

# Invoke your deployment
az deployment group create --name 'avm-bicep-vmexample1-deployment' --resource-group 'avm-bicep-vmexample1' --template-file '/<path-to>/VirtualMachineAVM_Example1/main.bicep'

The above commands will log you in to your Azure subscription, select a subscription to use, create a resource group, then deploy the main.bicep template to your resource group.

AVM Makes the deployment of Azure resources incredibly easy. Many of the parameters you would normally be required to define are taken care of by the AVM module itself. In fact, the location parameter is not even needed in your template—when left blank, by default, all AVM modules will deploy to the location in which your target Resource Group exists.

Now we have a Log Analytics workspace in our resource group which doesn’t do a whole lot of good on its own. Let’s take our template a step further by adding a Virtual Network that integrates with the Log Analytics workspace.

Virtual Network

We will now add a Virtual Network to our main.bicep file. This VNet will contain subnets and Network Security Groups (NSGs) for any of the resources we deploy that require IP addresses.

In your main.bicep file, add the following:

➕ Expand Code
 1module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 2  name: 'logAnalyticsWorkspace'
 3  params: {
 4    // Required parameters
 5    name: 'VM-AVM-Ex1-law'
 6    // Non-required parameters
 7    location: 'westus2'
 8  }
 9}
10
11module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
12  name: 'virtualNetworkDeployment'
13  params: {
14    // Required parameters
15    addressPrefixes: [
16      '10.0.0.0/16'
17    ]
18    name: 'VM-AVM-Ex1-vnet'
19    // Non-required parameters
20    location: 'westus2'
21  }
22}

Again, the Virtual Network AVM module requires only two things: a name and an addressPrefixes parameter.

Configure Diagnostics Settings

There is an additional parameter available in most AVM modules named diagnosticSettings. This parameter allows you to configure your resource to send its logs to any suitable logging service. In our case, we are using a Log Analytics workspace.

Let’s update our main.bicep file to have our VNet send all of its logging data to our Log Analytics workspace:

➕ Expand Code
 1module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 2  name: 'logAnalyticsWorkspace'
 3  params: {
 4    // Required parameters
 5    name: 'VM-AVM-Ex1-law'
 6    // Non-required parameters
 7    location: 'westus2'
 8  }
 9}
10
11module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
12  name: 'virtualNetworkDeployment'
13  params: {
14    // Required parameters
15    addressPrefixes: [
16      '10.0.0.0/16'
17    ]
18    name: 'VM-AVM-Ex1-vnet'
19    // Non-required parameters
20    location: 'westus2'
21    diagnosticSettings: [
22      {
23        name: 'vNetDiagnostics'
24        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
25      }
26    ]
27  }
28}

Notice how the diagnosticsSettings parameter needs a workspaceResourceId? All you need to do is add a reference to the built-in logAnalyticsWorkspaceId output of the logAnalyticsWorkspace AVM module. That’s it! Our VNet now has integrated its logging with our Log Analytics workspace. All AVM modules come with a set of built-in outputs that can be easily referenced by other modules within your template.

Info

All AVM modules have built-in outputs which can be referenced using the <moduleName>.outputs.<outputName> syntax.

When using plain Bicep, many of these outputs require multiple lines of code or knowledge of the correct object ID references to get at the desired output. AVM modules do much of this heavy lifting for you by taking care of these complex tasks within the module itself, then exposing them to you through the module’s outputs. Find out more about Bicep Outputs.

Add a Subnet and NAT Gateway

We can’t use a Virtual Network without subnets, so let’s add a subnet next. According to our Architecture, we will have three subnets: one for the Virtual Machine, one for the Bastion host, and one for Private Endpoints. We can start with the VM subnet for now. While we’re at it, let’s also add the NAT Gateway, the NAT Gateway’s Public IP, the attach the NAT Gateway to the VM subnet.

Add the following to your main.bicep:

➕ Expand Code
 1module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 2  name: 'logAnalyticsWorkspace'
 3  params: {
 4    // Required parameters
 5    name: 'VM-AVM-Ex1-law'
 6    // Non-required parameters
 7    location: 'westus2'
 8  }
 9}
10
11module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
12  name: 'natGwPublicIpDeployment'
13  params: {
14    // Required parameters
15    name: 'VM-AVM-Ex1-natgwpip'
16    // Non-required parameters
17    location: 'westus2'
18    diagnosticSettings: [
19      {
20        name: 'natGwPublicIpDiagnostics'
21        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
22      }
23    ]
24  }
25}
26
27module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
28  name: 'natGatewayDeployment'
29  params: {
30    // Required parameters
31    name: 'VM-AVM-Ex1-natGw'
32    zone: 1
33    // Non-required parameters
34    publicIpResourceIds: [
35      natGwPublicIp.outputs.resourceId
36    ]
37  }
38}
39
40module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
41  name: 'virtualNetworkDeployment'
42  params: {
43    // Required parameters
44    addressPrefixes: [
45      '10.0.0.0/16'
46    ]
47    name: 'VM-AVM-Ex1-vnet'
48    // Non-required parameters
49    location: 'westus2'
50    diagnosticSettings: [
51      {
52        name: 'vNetDiagnostics'
53        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
54      }
55    ]
56    subnets: [
57      {
58        name: 'VMSubnet'
59        addressPrefix: cidrSubnet('10.0.0.0/16', 24, 0) // first subnet in address space
60        natGatewayResourceId: natGateway.outputs.resourceId
61      }
62    ]
63  }
64}

The modification adds a subnets property to our virtualNetwork module. The AVM network/virtual-network module supports the creation of subnets directly within the module itself. We can also link our NAT Gateway directly to the subnet within this submodule.

A nice feature within Bicep are the various functions available. We use the cidrSubnet() function to declare CIDR blocks without having to calculate them on your own.

Switch to Parameters and Variables

See how we are reusing the same CIDR block 10.0.0.0/16 in multiple locations? You may have noticed we are defining the same location in two different spots as well. We’re now at a point in the development where we should leverage one of our first recommended practices: using parameters and variables!

Tip

Use Bicep variables to define values that will be constant and reused with your template; use parameters anywhere you may need a modifiable value.

Let’s enhance the template by adding variables for the CIDR block and prefix, then use a location parameter with a default value. We’ll then reference those in the module:

➕ Expand Code
 1param location string = 'westus2'
 2
 3var addressPrefix = '10.0.0.0/16'
 4var prefix = 'VM-AVM-Ex1'
 5
 6module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 7  name: 'logAnalyticsWorkspace'
 8  params: {
 9    // Required parameters
10    name: '${prefix}-law'
11    // Non-required parameters
12    location: location
13  }
14}
15
16module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
17  name: 'natGwPublicIpDeployment'
18  params: {
19    // Required parameters
20    name: '${prefix}-natgwpip'
21    // Non-required parameters
22    location: location
23    diagnosticSettings: [
24      {
25        name: 'natGwPublicIpDiagnostics'
26        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
27      }
28    ]
29  }
30}
31
32module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
33  name: 'natGatewayDeployment'
34  params: {
35    // Required parameters
36    name: '${prefix}-natgw'
37    zone: 1
38    // Non-required parameters
39    publicIpResourceIds: [
40      natGwPublicIp.outputs.resourceId
41    ]
42  }
43}
44
45module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
46  name: 'virtualNetworkDeployment'
47  params: {
48    // Required parameters
49    addressPrefixes: [
50      addressPrefix
51    ]
52    name: '${prefix}-vnet'
53    // Non-required parameters
54    location: location
55    diagnosticSettings: [
56      {
57        name: 'vNetDiagnostics'
58        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
59      }
60    ]
61    subnets: [
62      {
63        name: 'VMSubnet'
64        addressPrefix: cidrSubnet(addressPrefix, 24, 0) // first subnet in address space
65        natGatewayResourceId: natGateway.outputs.resourceId
66      }
67    ]
68  }
69}

We now have a good basis for the infrastructure to be utilized by the rest of the resources in our Architecture. We will come back to our networking in a future step, once we are ready to create some Network Security Groups. For now, let’s move on to other modules.

Key Vault

Key Vaults are one of the key components in most Azure architectures as they create a place where you can save and reference secrets in a secure manner (“secrets” in the general sense, as opposed to the secret object type in Key Vaults). The Key Vault AVM module makes it very simple to store secrets generated in your template. In this tutorial, we will use one of the most secure methods of storing and retrieving secrets by leveraging this Key Vault in our Bicep template.

The first step is easy: add the Key Vault AVM module to our main.bicep file. In addition, let’s also ensure it’s hooked into our Log Analytics workspace (we will do this for every new module from here on out).

➕ Expand Code
 1param location string = 'westus2'
 2
 3var addressPrefix = '10.0.0.0/16'
 4var prefix = 'VM-AVM-Ex1'
 5
 6module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 7  name: 'logAnalyticsWorkspace'
 8  params: {
 9    // Required parameters
10    name: '${prefix}-law'
11    // Non-required parameters
12    location: location
13  }
14}
15
16module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
17  name: 'natGwPublicIpDeployment'
18  params: {
19    // Required parameters
20    name: '${prefix}-natgwpip'
21    // Non-required parameters
22    location: location
23    diagnosticSettings: [
24      {
25        name: 'natGwPublicIpDiagnostics'
26        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
27      }
28    ]
29  }
30}
31
32module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
33  name: 'natGatewayDeployment'
34  params: {
35    // Required parameters
36    name: '${prefix}-natgw'
37    zone: 1
38    // Non-required parameters
39    publicIpResourceIds: [
40      natGwPublicIp.outputs.resourceId
41    ]
42  }
43}
44
45module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
46  name: 'virtualNetworkDeployment'
47  params: {
48    // Required parameters
49    addressPrefixes: [
50      addressPrefix
51    ]
52    name: '${prefix}-vnet'
53    // Non-required parameters
54    location: location
55    diagnosticSettings: [
56      {
57        name: 'vNetDiagnostics'
58        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
59      }
60    ]
61    subnets: [
62      {
63        name: 'VMSubnet'
64        addressPrefix: cidrSubnet(addressPrefix, 24, 0) // first subnet in address space
65        natGatewayResourceId: natGateway.outputs.resourceId
66      }
67    ]
68  }
69}
70
71module keyVault 'br/public:avm/res/key-vault/vault:0.12.1' = {
72  name: 'keyVaultDeployment'
73  params: {
74    // Required parameters
75    name: '${uniqueString(resourceGroup().id)}-kv'
76    // Non-required parameters
77    location: location
78    diagnosticSettings: [
79      {
80        name: 'keyVaultDiagnostics'
81        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
82      }
83    ]
84  }
85}

The name of the Key Vault we will deploy uses the uniqueString() Bicep function. Key Vault names must be globally unique. We will therefore deviate from our standard naming convention thus far and make an exception for the Key Vault. Note how we are still adding a suffix to the Key Vault name, so its name remains recognizable; you can use a combination of concatenating unique strings, prefixes, or suffixes to follow your own naming standard preferences.

When we generate our unique string, we will pass in the resourceGroup().id as the seed for the uniqueString() function so that every time you deploy this main.bicep to the same resource group, it will use the same randomly generated name for your Key Vault (since resourceGroup().id will be the same).

Tip

Bicep has many built-in functions available. We used two here: uniqueString() and resourceGroup(). The resourceGroup(), subscription(), and deployment() functions are very useful when seeding uniqueString() or guid() functions. Just be cautious about name length limitations for each Azure service! Visit this page to learn more about Bicep functions.

We will use this Key Vault later on when we create a VM and need to store its password. Now that we have it, a Virtual Network, Subnet, and Log Analytics prepared, we should have everything we need to deploy a Virtual Machine!

Info

In the future, we will update this guide to show how to generate and store a certificate in the Key Vault, then use that certificate to authenticate into the Virtual Machine.

Virtual Machine

Warning

The AVM Virtual Machine module enables the EncryptionAtHost feature by default. You must enable this feature within your Azure subscription successfully deploy this example code. To do so, run the following:

Deploy with
# Wait a few minutes after running the command to allow it to propagate
Register-AzProviderFeature -FeatureName "EncryptionAtHost" -ProviderNamespace "Microsoft.Compute"
az feature register --namespace Microsoft.Compute --name EncryptionAtHost

# Propagate the change
az provider register --namespace Microsoft.Compute

For our Virtual Machine (VM) deployment, we need to add the following to our main.bicep file:

➕ Expand Code
  1param location string = 'westus2'
  2
  3// START add-password-param
  4@description('Required. A password for the VM admin user.')
  5@secure()
  6param vmAdminPass string
  7// END add-password-param
  8
  9var addressPrefix = '10.0.0.0/16'
 10var prefix = 'VM-AVM-Ex1'
 11
 12module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 13  name: 'logAnalyticsWorkspace'
 14  params: {
 15    // Required parameters
 16    name: '${prefix}-law'
 17    // Non-required parameters
 18    location: location
 19  }
 20}
 21
 22module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
 23  name: 'natGwPublicIpDeployment'
 24  params: {
 25    // Required parameters
 26    name: '${prefix}-natgwpip'
 27    // Non-required parameters
 28    location: location
 29    diagnosticSettings: [
 30      {
 31        name: 'natGwPublicIpDiagnostics'
 32        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 33      }
 34    ]
 35  }
 36}
 37
 38module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
 39  name: 'natGatewayDeployment'
 40  params: {
 41    // Required parameters
 42    name: '${prefix}-natgw'
 43    zone: 1
 44    // Non-required parameters
 45    publicIpResourceIds: [
 46      natGwPublicIp.outputs.resourceId
 47    ]
 48  }
 49}
 50
 51module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
 52  name: 'virtualNetworkDeployment'
 53  params: {
 54    // Required parameters
 55    addressPrefixes: [
 56      addressPrefix
 57    ]
 58    name: '${prefix}-vnet'
 59    // Non-required parameters
 60    location: location
 61    diagnosticSettings: [
 62      {
 63        name: 'vNetDiagnostics'
 64        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 65      }
 66    ]
 67    subnets: [
 68      {
 69        name: 'VMSubnet'
 70        addressPrefix: cidrSubnet(addressPrefix, 24, 0) // first subnet in address space
 71        natGatewayResourceId: natGateway.outputs.resourceId
 72      }
 73    ]
 74  }
 75}
 76
 77module keyVault 'br/public:avm/res/key-vault/vault:0.12.1' = {
 78  name: 'keyVaultDeployment'
 79  params: {
 80    // Required parameters
 81    name: '${uniqueString(resourceGroup().id)}-kv'
 82    // Non-required parameters
 83    location: location
 84    diagnosticSettings: [
 85      {
 86        name: 'keyVaultDiagnostics'
 87        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 88      }
 89    ]
 90    // START add-keyvault-secret
 91    secrets: [
 92      {
 93        name: 'vmAdminPassword'
 94        value: vmAdminPass
 95      }
 96    ]
 97    // END add-keyvault-secret
 98  }
 99}
100
101module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.13.1' = {
102  name: 'linuxVirtualMachineDeployment'
103  params: {
104    // Required parameters
105    adminUsername: 'localAdminUser'
106    adminPassword: vmAdminPass
107    imageReference: {
108      offer: '0001-com-ubuntu-server-jammy'
109      publisher: 'Canonical'
110      sku: '22_04-lts-gen2'
111      version: 'latest'
112    }
113    name: '${prefix}-vm1'
114    // START vm-subnet-reference
115    nicConfigurations: [
116      {
117        ipConfigurations: [
118          {
119            name: 'ipconfig01'
120            subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] // VMSubnet
121          }
122        ]
123        nicSuffix: '-nic-01'
124      }
125    ]
126    // END vm-subnet-reference
127    osDisk: {
128      caching: 'ReadWrite'
129      diskSizeGB: 128
130      managedDisk: {
131        storageAccountType: 'Standard_LRS'
132      }
133    }
134    osType: 'Linux'
135    vmSize: 'Standard_B2s_v2'
136    zone: 0
137    // Non-required parameters
138    location: location
139  }
140}

The VM module is one of the more complex modules in AVM—behind the scenes, it takes care of a lot of heavy lifting that, without AVM, would require multiple Bicep resources to be deployed and referenced.

For example, look at the nicConfigurations parameter: normally, you would need to deploy a separate NIC resource, which itself also requires an IP resource, then attach them to each other, and finally, attach them all to your VM.

With the AVM VM module, the nicConfigurations parameter accepts an object, allowing you to create any number of NICs to attach to your VM from within the VM resource deployment itself. It handles all the naming, creation of other necessary dependencies, and attaches them all together, so you don’t have to. The osDisk parameter is similar, though slightly less complex. There are many more parameters within the VM module that you can leverage if needed, that share a similar ease-of-use.

Since this is the real highlight of our main.bicep file, we need to take a closer look at some of the other changes that were made.

  • VM Admin Password Parameter

    1@description('Required. A password for the VM admin user.')
    2@secure()
    3param vmAdminPass string

    First, we added a new parameter. The value of this will be provided when the main.bicep template is deployed. We don’t want any passwords stored as text in code; for our purposes, the safest way to do this is to prompt the end user for the password at the time of deployment.

    Warning

    The supplied password must be between 6-72 characters long and must satisfy at least 3 of password complexity requirements from the following: Contains an uppercase character; Contains a lowercase character; Contains a numeric digit; Contains a special character. Control characters are not allowed

    Also note how we are using the @secure() decorator on the password parameter. This will ensure the value of the password is never displayed in any of the deployment logs or in Azure. We have also added the @description() decorator and started the description with “Required.” It’s a good habit and recommended practice to document your parameters in Bicep. This will ensure that VS Code’s built-in Bicep linter can provide end-users insightful information when deploying your Bicep templates.

    Info

    Always use the @secure() decorator when creating a parameter that will hold sensitive data!

  • Add the VM Admin Password to Key Vault

    1    secrets: [
    2      {
    3        name: 'vmAdminPassword'
    4        value: vmAdminPass
    5      }
    6    ]

    The next thing we have done is save the value of our vmAdminPass parameter to our Key Vault. We have done this by adding a secrets parameter to the Key Vault module. Adding secrets to Key Vaults is very simple when using the AVM module.

    By adding our password to the Key Vault, it will ensure that we never lose the password and that it is stored securely. As long as a user has appropriate permissions on the vault, the password can be fetched easily.

  • Reference the VM Subnet

     1    nicConfigurations: [
     2      {
     3        ipConfigurations: [
     4          {
     5            name: 'ipconfig01'
     6            subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] // VMSubnet
     7          }
     8        ]
     9        nicSuffix: '-nic-01'
    10      }
    11    ]

    Here, we reference another built-in output, this time from the AVM Virtual Network module. This example shows how to use an output that is part of an array. When the Virtual Network module creates subnets, it automatically creates a set of pre-defined outputs for them, one of which is an array that contains each subnet’s subnetResourceId. Our VM Subnet was the first one created which is position [0] in the array.

    Other AVM modules may make use of arrays to store outputs. If you are unsure what type of outputs a module provides, you can always reference the Outputs section of each module’s README.md.

Storage Account

The last major component we need to add is a Storage Account. Because this Storage Account will be used as a backend storage to hold blobs for the hypothetical application that runs on our VM, we’ll also create a blob container within it using the same AVM Storage Account module.

➕ Expand Code
  1param location string = 'westus2'
  2
  3@description('Required. A password for the VM admin user.')
  4@secure()
  5param vmAdminPass string
  6
  7var addressPrefix = '10.0.0.0/16'
  8var prefix = 'VM-AVM-Ex1'
  9
 10module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 11  name: 'logAnalyticsWorkspace'
 12  params: {
 13    // Required parameters
 14    name: '${prefix}-law'
 15    // Non-required parameters
 16    location: location
 17  }
 18}
 19
 20module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
 21  name: 'natGwPublicIpDeployment'
 22  params: {
 23    // Required parameters
 24    name: '${prefix}-natgwpip'
 25    // Non-required parameters
 26    location: location
 27    diagnosticSettings: [
 28      {
 29        name: 'natGwPublicIpDiagnostics'
 30        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 31      }
 32    ]
 33  }
 34}
 35
 36module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
 37  name: 'natGatewayDeployment'
 38  params: {
 39    // Required parameters
 40    name: '${prefix}-natgw'
 41    zone: 1
 42    // Non-required parameters
 43    publicIpResourceIds: [
 44      natGwPublicIp.outputs.resourceId
 45    ]
 46  }
 47}
 48
 49module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
 50  name: 'virtualNetworkDeployment'
 51  params: {
 52    // Required parameters
 53    addressPrefixes: [
 54      addressPrefix
 55    ]
 56    name: '${prefix}-vnet'
 57    // Non-required parameters
 58    location: location
 59    diagnosticSettings: [
 60      {
 61
 62        name: 'vNetDiagnostics'
 63        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 64      }
 65    ]
 66    subnets: [
 67      {
 68        name: 'VMSubnet'
 69        addressPrefix: cidrSubnet(addressPrefix, 24, 0) // first subnet in address space
 70        natGatewayResourceId: natGateway.outputs.resourceId
 71      }
 72    ]
 73  }
 74}
 75
 76module keyVault 'br/public:avm/res/key-vault/vault:0.12.1' = {
 77  name: 'keyVaultDeployment'
 78  params: {
 79    // Required parameters
 80    name: '${uniqueString(resourceGroup().id)}-kv'
 81    // Non-required parameters
 82    location: location
 83    diagnosticSettings: [
 84      {
 85        name: 'keyVaultDiagnostics'
 86        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 87      }
 88    ]
 89    enablePurgeProtection: false // disable purge protection for this example so we can more easily delete it
 90    secrets: [
 91      {
 92        name: 'vmAdminPassword'
 93        value: vmAdminPass
 94      }
 95    ]
 96  }
 97}
 98
 99module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.13.1' = {
100  name: 'linuxVirtualMachineDeployment'
101  params: {
102    // Required parameters
103    adminUsername: 'localAdminUser'
104    adminPassword: vmAdminPass
105    imageReference: {
106      offer: '0001-com-ubuntu-server-jammy'
107      publisher: 'Canonical'
108      sku: '22_04-lts-gen2'
109      version: 'latest'
110    }
111    name: '${prefix}-vm1'
112    nicConfigurations: [
113      {
114        ipConfigurations: [
115          {
116            name: 'ipconfig01'
117            subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] // VMSubnet
118          }
119        ]
120        nicSuffix: '-nic-01'
121      }
122    ]
123    osDisk: {
124      caching: 'ReadWrite'
125      diskSizeGB: 128
126      managedDisk: {
127        storageAccountType: 'Standard_LRS'
128      }
129    }
130
131    osType: 'Linux'
132    vmSize: 'Standard_B2s_v2'
133    zone: 0
134    // Non-required parameters
135    location: location
136  }
137}
138
139module storageAccount 'br/public:avm/res/storage/storage-account:0.19.0' = {
140  name: 'storageAccountDeployment'
141  params: {
142    // Required parameters
143    name: '${uniqueString(resourceGroup().id)}sa'
144    // Non-required parameters
145    location: location
146    skuName: 'Standard_LRS'
147    diagnosticSettings: [
148      {
149        name: 'storageAccountDiagnostics'
150        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
151      }
152    ]
153    blobServices: {
154      containers: [
155        {
156          name: 'vmstorage'
157          publicAccess: 'None'
158        }
159      ]
160    }
161  }
162}

We now have all the major components of our Architecture diagram built!

The last steps we need to take to meet our requirements is to ensure our networking resources are secure and that we are using least privileged access by leveraging Role-Based Access Control (RBAC). Let’s get to it!

Network Security Groups

We’ll add a Network Security Group (NSG) to our VM subnet. This will act as a layer 3 and layer 4 firewall for networked resources. This implementation includes an appropriate inbound rule to allow SSH traffic from the Bastion host:

➕ Expand Code
  1param location string = 'westus2'
  2
  3@description('Required. A password for the VM admin user.')
  4@secure()
  5param vmAdminPass string
  6
  7var addressPrefix = '10.0.0.0/16'
  8var prefix = 'VM-AVM-Ex1'
  9
 10module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 11  name: 'logAnalyticsWorkspace'
 12  params: {
 13    // Required parameters
 14    name: '${prefix}-law'
 15    // Non-required parameters
 16    location: location
 17  }
 18}
 19
 20module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
 21  name: 'natGwPublicIpDeployment'
 22  params: {
 23    // Required parameters
 24    name: '${prefix}-natgwpip'
 25    // Non-required parameters
 26    location: location
 27    diagnosticSettings: [
 28      {
 29        name: 'natGwPublicIpDiagnostics'
 30        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 31      }
 32    ]
 33  }
 34}
 35
 36module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
 37  name: 'natGatewayDeployment'
 38  params: {
 39    // Required parameters
 40    name: '${prefix}-natgw'
 41    zone: 1
 42    // Non-required parameters
 43    publicIpResourceIds: [
 44      natGwPublicIp.outputs.resourceId
 45    ]
 46  }
 47}
 48
 49module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
 50  name: 'virtualNetworkDeployment'
 51  params: {
 52    // Required parameters
 53    addressPrefixes: [
 54      addressPrefix
 55    ]
 56    name: '${prefix}-vnet'
 57    // Non-required parameters
 58    location: location
 59    diagnosticSettings: [
 60      {
 61        name: 'vNetDiagnostics'
 62        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 63      }
 64    ]
 65    subnets: [
 66      {
 67        name: 'VMSubnet'
 68        addressPrefix: cidrSubnet(addressPrefix, 24, 0) // first subnet in address space
 69        natGatewayResourceId: natGateway.outputs.resourceId
 70        networkSecurityGroupResourceId: nsgVM.outputs.resourceId
 71      }
 72    ]
 73  }
 74}
 75
 76module nsgVM 'br/public:avm/res/network/network-security-group:0.5.1' = {
 77  name: 'nsgVmDeployment'
 78  params: {
 79    name: '${prefix}-NSG-VM'
 80    location: location
 81    securityRules: [
 82      {
 83        name: 'AllowBastionSSH'
 84        properties: {
 85          access: 'Allow'
 86          direction: 'Inbound'
 87          priority: 100
 88          protocol: 'Tcp'
 89          sourceAddressPrefix: 'virtualNetwork'
 90          sourcePortRange: '*'
 91          destinationAddressPrefix: '*'
 92          destinationPortRange: '22'
 93        }
 94      }
 95    ]
 96  }
 97}
 98
 99module keyVault 'br/public:avm/res/key-vault/vault:0.12.1' = {
100  name: 'keyVaultDeployment'
101  params: {
102    // Required parameters
103    name: '${uniqueString(resourceGroup().id)}-kv'
104    // Non-required parameters
105    location: location
106    diagnosticSettings: [
107      {
108        name: 'keyVaultDiagnostics'
109        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
110      }
111    ]
112    enablePurgeProtection: false // disable purge protection for this example so we can more easily delete it
113    secrets: [
114      {
115        name: 'vmAdminPassword'
116        value: vmAdminPass
117      }
118    ]
119  }
120}
121
122module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.13.1' = {
123  name: 'linuxVirtualMachineDeployment'
124  params: {
125    // Required parameters
126    adminUsername: 'localAdminUser'
127    adminPassword: vmAdminPass
128    imageReference: {
129      offer: '0001-com-ubuntu-server-jammy'
130      publisher: 'Canonical'
131      sku: '22_04-lts-gen2'
132      version: 'latest'
133    }
134    name: '${prefix}-vm1'
135    nicConfigurations: [
136      {
137        ipConfigurations: [
138          {
139            name: 'ipconfig01'
140            subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] // VMSubnet
141          }
142        ]
143        nicSuffix: '-nic-01'
144      }
145    ]
146    osDisk: {
147      caching: 'ReadWrite'
148      diskSizeGB: 128
149      managedDisk: {
150        storageAccountType: 'Standard_LRS'
151      }
152    }
153
154    osType: 'Linux'
155    vmSize: 'Standard_B2s_v2'
156    zone: 0
157    // Non-required parameters
158    location: location
159  }
160}
161
162module storageAccount 'br/public:avm/res/storage/storage-account:0.19.0' = {
163  name: 'storageAccountDeployment'
164  params: {
165    // Required parameters
166    name: '${uniqueString(resourceGroup().id)}sa'
167    // Non-required parameters
168    location: location
169    skuName: 'Standard_LRS'
170    diagnosticSettings: [
171      {
172        name: 'storageAccountDiagnostics'
173        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
174      }
175    ]
176    blobServices: {
177      containers: [
178        {
179          name: 'vmstorage'
180          publicAccess: 'None'
181        }
182      ]
183    }
184  }
185}

Disable Public Access to Storage Account

Since the Storage Account serves as a backend resource exclusively for the Virtual Machine, it will be secured as much as possible. This involves adding a Private Endpoint and disabling public internet access. AVM makes creation and assignment of Private Endpoints to resources incredibly easy. Take a look:

➕ Expand Code
  1param location string = 'westus2'
  2
  3@description('Required. A password for the VM admin user.')
  4@secure()
  5param vmAdminPass string
  6
  7var addressPrefix = '10.0.0.0/16'
  8var prefix = 'VM-AVM-Ex1'
  9
 10module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 11  name: 'logAnalyticsWorkspace'
 12  params: {
 13    // Required parameters
 14    name: '${prefix}-law'
 15    // Non-required parameters
 16    location: location
 17  }
 18}
 19
 20module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
 21  name: 'natGwPublicIpDeployment'
 22  params: {
 23    // Required parameters
 24    name: '${prefix}-natgwpip'
 25    // Non-required parameters
 26    location: location
 27    diagnosticSettings: [
 28      {
 29        name: 'natGwPublicIpDiagnostics'
 30        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 31      }
 32    ]
 33  }
 34}
 35
 36module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
 37  name: 'natGatewayDeployment'
 38  params: {
 39    // Required parameters
 40    name: '${prefix}-natgw'
 41    zone: 1
 42    // Non-required parameters
 43    publicIpResourceIds: [
 44      natGwPublicIp.outputs.resourceId
 45    ]
 46  }
 47}
 48
 49module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
 50  name: 'virtualNetworkDeployment'
 51  params: {
 52    // Required parameters
 53    addressPrefixes: [
 54      addressPrefix
 55    ]
 56    name: '${prefix}-vnet'
 57    // Non-required parameters
 58    location: location
 59    diagnosticSettings: [
 60      {
 61        name: 'vNetDiagnostics'
 62        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 63      }
 64    ]
 65    subnets: [
 66      {
 67        name: 'VMSubnet'
 68        addressPrefix: cidrSubnet(addressPrefix, 24, 0) // first subnet in address space
 69        natGatewayResourceId: natGateway.outputs.resourceId
 70        networkSecurityGroupResourceId: nsgVM.outputs.resourceId
 71      }
 72      {
 73        name: 'PrivateEndpointSubnet'
 74        addressPrefix: cidrSubnet(addressPrefix, 24, 1) // second subnet in address space
 75      }
 76    ]
 77  }
 78}
 79
 80module nsgVM 'br/public:avm/res/network/network-security-group:0.5.1' = {
 81  name: 'nsgVmDeployment'
 82  params: {
 83    name: '${prefix}-NSG-VM'
 84    location: location
 85    securityRules: [
 86      {
 87        name: 'AllowBastionSSH'
 88        properties: {
 89          access: 'Allow'
 90          direction: 'Inbound'
 91          priority: 100
 92          protocol: 'Tcp'
 93          sourceAddressPrefix: 'virtualNetwork'
 94          sourcePortRange: '*'
 95          destinationAddressPrefix: '*'
 96          destinationPortRange: '22'
 97        }
 98      }
 99    ]
100  }
101}
102
103module keyVault 'br/public:avm/res/key-vault/vault:0.12.1' = {
104  name: 'keyVaultDeployment'
105  params: {
106    // Required parameters
107    name: '${uniqueString(resourceGroup().id)}-kv'
108    // Non-required parameters
109    location: location
110    diagnosticSettings: [
111      {
112        name: 'keyVaultDiagnostics'
113        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
114      }
115    ]
116    enablePurgeProtection: false // disable purge protection for this example so we can more easily delete it
117    secrets: [
118      {
119        name: 'vmAdminPassword'
120        value: vmAdminPass
121      }
122    ]
123  }
124}
125
126module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.14.0' = {
127  name: 'linuxVirtualMachineDeployment'
128  params: {
129    // Required parameters
130    adminUsername: 'localAdminUser'
131    adminPassword: vmAdminPass
132    imageReference: {
133      offer: '0001-com-ubuntu-server-jammy'
134      publisher: 'Canonical'
135      sku: '22_04-lts-gen2'
136      version: 'latest'
137    }
138    name: '${prefix}-vm1'
139    nicConfigurations: [
140      {
141        ipConfigurations: [
142          {
143            name: 'ipconfig01'
144            subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] // VMSubnet
145          }
146        ]
147        nicSuffix: '-nic-01'
148      }
149    ]
150    osDisk: {
151      caching: 'ReadWrite'
152      diskSizeGB: 128
153      managedDisk: {
154        storageAccountType: 'Standard_LRS'
155      }
156    }
157    osType: 'Linux'
158    vmSize: 'Standard_B2s_v2'
159    zone: 0
160    // Non-required parameters
161    location: location
162  }
163}
164
165module storageAccount 'br/public:avm/res/storage/storage-account:0.19.0' = {
166  name: 'storageAccountDeployment'
167  params: {
168    // Required parameters
169    name: '${uniqueString(resourceGroup().id)}sa'
170    // Non-required parameters
171    location: location
172    skuName: 'Standard_LRS'
173    diagnosticSettings: [
174      {
175        name: 'storageAccountDiagnostics'
176        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
177      }
178    ]
179    publicNetworkAccess: 'Disabled'
180    allowBlobPublicAccess: false
181    blobServices: {
182      containers: [
183        {
184          name: 'vmstorage'
185          publicAccess: 'None'
186        }
187      ]
188    }
189    privateEndpoints: [
190      {
191        service: 'Blob'
192        subnetResourceId: virtualNetwork.outputs.subnetResourceIds[1] // Private Endpoint Subnet
193        privateDnsZoneGroup: {
194          privateDnsZoneGroupConfigs: [
195            {
196              privateDnsZoneResourceId: privateDnsBlob.outputs.resourceId
197            }
198          ]
199        }
200      }
201    ]
202  }
203}
204
205module privateDnsBlob 'br/public:avm/res/network/private-dns-zone:0.7.1' = {
206  name: '${prefix}-privatedns-blob'
207  params: {
208    name: 'privatelink.blob.${environment().suffixes.storage}'
209    location: 'global'
210    virtualNetworkLinks: [
211      {
212        name: '${virtualNetwork.outputs.name}-vnetlink'
213        virtualNetworkResourceId: virtualNetwork.outputs.resourceId
214      }
215    ]
216  }
217}

This implementation adds a dedicated subnet for Private Endpoints following the recommended practice of isolating Private Endpoints in their own subnet.

The addition of just a few lines of code in the privateEndpoints parameter handles the complex tasks of creating the Private Endpoint, associating it with the VNet, and attaching it to the resource. AVM drastically simplifies the creation of Private Endpoints for just about every Azure Resource that supports them.

The implementation also disables all public network connectivity to the Storage Account, ensuring it only accepts traffic via the Private Endpoint.

Finally, a Private DNS zone is added and linked to the VNet, enabling the VM to resolve the Private IP address associated with the Storage Account.

Bastion

To securely access the Virtual Machine without exposing its SSH port to the public internet, we’ll create an Azure Bastion host. The Bastion Host requires a subnet with the exact name AzureBastionSubnet which cannot contain anything other than Bastion Hosts.

➕ Expand Code
  1param location string = 'westus2'
  2
  3@description('Required. A password for the VM admin user.')
  4@secure()
  5param vmAdminPass string
  6
  7var addressPrefix = '10.0.0.0/16'
  8var prefix = 'VM-AVM-Ex1'
  9
 10module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 11  name: 'logAnalyticsWorkspace'
 12  params: {
 13    // Required parameters
 14    name: '${prefix}-law'
 15    // Non-required parameters
 16    location: location
 17  }
 18}
 19
 20module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
 21  name: 'natGwPublicIpDeployment'
 22  params: {
 23    // Required parameters
 24    name: '${prefix}-natgwpip'
 25    // Non-required parameters
 26    location: location
 27    diagnosticSettings: [
 28      {
 29        name: 'natGwPublicIpDiagnostics'
 30        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 31      }
 32    ]
 33  }
 34}
 35
 36module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
 37  name: 'natGatewayDeployment'
 38  params: {
 39    // Required parameters
 40    name: '${prefix}-natgw'
 41    zone: 1
 42    // Non-required parameters
 43    publicIpResourceIds: [
 44      natGwPublicIp.outputs.resourceId
 45    ]
 46  }
 47}
 48
 49module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
 50  name: 'virtualNetworkDeployment'
 51  params: {
 52    // Required parameters
 53    addressPrefixes: [
 54      addressPrefix
 55    ]
 56    name: '${prefix}-vnet'
 57    // Non-required parameters
 58    location: location
 59    diagnosticSettings: [
 60      {
 61        name: 'vNetDiagnostics'
 62        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 63      }
 64    ]
 65    subnets: [
 66      {
 67        name: 'VMSubnet'
 68        addressPrefix: cidrSubnet(addressPrefix, 24, 0) // first subnet in address space
 69        natGatewayResourceId: natGateway.outputs.resourceId
 70        networkSecurityGroupResourceId: nsgVM.outputs.resourceId
 71      }
 72      {
 73        name: 'PrivateEndpointSubnet'
 74        addressPrefix: cidrSubnet(addressPrefix, 24, 1) // second subnet in address space
 75      }
 76      {
 77        name: 'AzureBastionSubnet' // Azure Bastion Host requires this subnet to be named exactly "AzureBastionSubnet"
 78        addressPrefix: cidrSubnet(addressPrefix, 24, 2) // third subnet in address space
 79      }
 80    ]
 81  }
 82}
 83
 84module nsgVM 'br/public:avm/res/network/network-security-group:0.5.1' = {
 85  name: 'nsgVmDeployment'
 86  params: {
 87    name: '${prefix}-NSG-VM'
 88    location: location
 89    securityRules: [
 90      {
 91        name: 'AllowBastionSSH'
 92        properties: {
 93          access: 'Allow'
 94          direction: 'Inbound'
 95          priority: 100
 96          protocol: 'Tcp'
 97          sourceAddressPrefix: 'virtualNetwork'
 98          sourcePortRange: '*'
 99          destinationAddressPrefix: '*'
100          destinationPortRange: '22'
101        }
102      }
103    ]
104  }
105}
106
107module keyVault 'br/public:avm/res/key-vault/vault:0.12.1' = {
108  name: 'keyVaultDeployment'
109  params: {
110    // Required parameters
111    name: '${uniqueString(resourceGroup().id)}-kv'
112    // Non-required parameters
113    location: location
114    diagnosticSettings: [
115      {
116        name: 'keyVaultDiagnostics'
117        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
118      }
119    ]
120    enablePurgeProtection: false // disable purge protection for this example so we can more easily delete it
121    secrets: [
122      {
123        name: 'vmAdminPassword'
124        value: vmAdminPass
125      }
126    ]
127  }
128}
129
130module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.14.0' = {
131  name: 'linuxVirtualMachineDeployment'
132  params: {
133    // Required parameters
134    adminUsername: 'localAdminUser'
135    adminPassword: vmAdminPass
136    imageReference: {
137      offer: '0001-com-ubuntu-server-jammy'
138      publisher: 'Canonical'
139      sku: '22_04-lts-gen2'
140      version: 'latest'
141    }
142    name: '${prefix}-vm1'
143    nicConfigurations: [
144      {
145        ipConfigurations: [
146          {
147            name: 'ipconfig01'
148            subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] // VMSubnet
149          }
150        ]
151        nicSuffix: '-nic-01'
152      }
153    ]
154    osDisk: {
155      caching: 'ReadWrite'
156      diskSizeGB: 128
157      managedDisk: {
158        storageAccountType: 'Standard_LRS'
159      }
160    }
161    osType: 'Linux'
162    vmSize: 'Standard_B2s_v2'
163    zone: 0
164    // Non-required parameters
165    location: location
166  }
167}
168
169module storageAccount 'br/public:avm/res/storage/storage-account:0.19.0' = {
170  name: 'storageAccountDeployment'
171  params: {
172    // Required parameters
173    name: '${uniqueString(resourceGroup().id)}sa'
174    // Non-required parameters
175    location: location
176    skuName: 'Standard_LRS'
177    diagnosticSettings: [
178      {
179        name: 'storageAccountDiagnostics'
180        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
181      }
182    ]
183    publicNetworkAccess: 'Disabled'
184    allowBlobPublicAccess: false
185    blobServices: {
186      containers: [
187        {
188          name: 'vmstorage'
189          publicAccess: 'None'
190        }
191      ]
192    }
193    privateEndpoints: [
194      {
195        service: 'Blob'
196        subnetResourceId: virtualNetwork.outputs.subnetResourceIds[1] // Private Endpoint Subnet
197        privateDnsZoneGroup: {
198          privateDnsZoneGroupConfigs: [
199            {
200              privateDnsZoneResourceId: privateDnsBlob.outputs.resourceId
201            }
202          ]
203        }
204      }
205    ]
206  }
207}
208
209module privateDnsBlob 'br/public:avm/res/network/private-dns-zone:0.7.1' = {
210  name: '${prefix}-privatedns-blob'
211  params: {
212    name: 'privatelink.blob.${environment().suffixes.storage}'
213    location: 'global'
214    virtualNetworkLinks: [
215      {
216        name: '${virtualNetwork.outputs.name}-vnetlink'
217        virtualNetworkResourceId: virtualNetwork.outputs.resourceId
218      }
219    ]
220  }
221}
222
223// Note: Deploying a Bastion Host will automatically create a Public IP and use the subnet named "AzureBastionSubnet"
224// within our VNet. This subnet is required and must be named exactly "AzureBastionSubnet" for the Bastion Host to work.
225module bastion 'br/public:avm/res/network/bastion-host:0.6.1' = {
226  name: 'bastionDeployment'
227  params: {
228    name: '${prefix}-bastion'
229    virtualNetworkResourceId: virtualNetwork.outputs.resourceId
230    skuName: 'Basic'
231    location: location
232    diagnosticSettings: [
233      {
234        name: 'bastionDiagnostics'
235        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
236      }
237    ]
238  }
239}

This simple addition of the bastion-host AVM module completes the secure access component of our architecture. You can now access the Virtual Machine by way of the Bastion Host in the Azure Portal.

Role-Based Access Control

To complete our solution, we have one final task: to apply Role-Based Access Control (RBAC) restrictions on our services, namely the Key Vault and Storage Account. The goal is to explicitly allow only the Virtual Machine to have Create, Read, Update, or Delete (CRUD) permissions on these two services.

This is accomplished by enabling a System-assigned Managed Identity on the Virtual Machine, then granting the VM’s Managed Identity appropriate permissions on the Storage Account and Key Vault:

➕ Expand Code
  1param location string = 'westus2'
  2
  3@description('Required. A password for the VM admin user.')
  4@secure()
  5param vmAdminPass string
  6
  7var addressPrefix = '10.0.0.0/16'
  8var prefix = 'VM-AVM-Ex1'
  9
 10module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 11  name: 'logAnalyticsWorkspace'
 12  params: {
 13    // Required parameters
 14    name: '${prefix}-law'
 15    // Non-required parameters
 16    location: location
 17  }
 18}
 19
 20module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
 21  name: 'natGwPublicIpDeployment'
 22  params: {
 23    // Required parameters
 24    name: '${prefix}-natgwpip'
 25    // Non-required parameters
 26    location: location
 27    diagnosticSettings: [
 28      {
 29        name: 'natGwPublicIpDiagnostics'
 30        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 31      }
 32    ]
 33  }
 34}
 35
 36module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
 37  name: 'natGatewayDeployment'
 38  params: {
 39    // Required parameters
 40    name: '${prefix}-natgw'
 41    zone: 1
 42    // Non-required parameters
 43    publicIpResourceIds: [
 44      natGwPublicIp.outputs.resourceId
 45    ]
 46  }
 47}
 48
 49module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
 50  name: 'virtualNetworkDeployment'
 51  params: {
 52    // Required parameters
 53    addressPrefixes: [
 54      addressPrefix
 55    ]
 56    name: '${prefix}-vnet'
 57    // Non-required parameters
 58    location: location
 59    diagnosticSettings: [
 60      {
 61        name: 'vNetDiagnostics'
 62        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 63      }
 64    ]
 65    subnets: [
 66      {
 67        name: 'VMSubnet'
 68        addressPrefix: cidrSubnet(addressPrefix, 24, 0) // first subnet in address space
 69        natGatewayResourceId: natGateway.outputs.resourceId
 70        networkSecurityGroupResourceId: nsgVM.outputs.resourceId
 71      }
 72      {
 73        name: 'PrivateEndpointSubnet'
 74        addressPrefix: cidrSubnet(addressPrefix, 24, 1) // second subnet in address space
 75      }
 76      {
 77        name: 'AzureBastionSubnet' // Azure Bastion Host requires this subnet to be named exactly "AzureBastionSubnet"
 78        addressPrefix: cidrSubnet(addressPrefix, 24, 2) // third subnet in address space
 79      }
 80    ]
 81  }
 82}
 83
 84module nsgVM 'br/public:avm/res/network/network-security-group:0.5.1' = {
 85  name: 'nsgVmDeployment'
 86  params: {
 87    name: '${prefix}-NSG-VM'
 88    location: location
 89    securityRules: [
 90      {
 91        name: 'AllowBastionSSH'
 92        properties: {
 93          access: 'Allow'
 94          direction: 'Inbound'
 95          priority: 100
 96          protocol: 'Tcp'
 97          sourceAddressPrefix: 'virtualNetwork'
 98          sourcePortRange: '*'
 99          destinationAddressPrefix: '*'
100          destinationPortRange: '22'
101        }
102      }
103    ]
104  }
105}
106
107module keyVault 'br/public:avm/res/key-vault/vault:0.12.1' = {
108  name: 'keyVaultDeployment'
109  params: {
110    // Required parameters
111    name: '${uniqueString(resourceGroup().id)}-kv'
112    // Non-required parameters
113    location: location
114    diagnosticSettings: [
115      {
116        name: 'keyVaultDiagnostics'
117        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
118      }
119    ]
120    enablePurgeProtection: false // disable purge protection for this example so we can more easily delete it
121    secrets: [
122      {
123        name: 'vmAdminPassword'
124        value: vmAdminPass
125      }
126    ]
127    roleAssignments: [
128      {
129        principalId: virtualMachine.outputs.systemAssignedMIPrincipalId
130        principalType: 'ServicePrincipal'
131        roleDefinitionIdOrName: 'Key Vault Secrets User' // Allows read access to secrets
132      }
133    ]
134  }
135}
136
137module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.14.0' = {
138  name: 'linuxVirtualMachineDeployment'
139  params: {
140    // Required parameters
141    adminUsername: 'localAdminUser'
142    adminPassword: vmAdminPass
143    imageReference: {
144      offer: '0001-com-ubuntu-server-jammy'
145      publisher: 'Canonical'
146      sku: '22_04-lts-gen2'
147      version: 'latest'
148    }
149    name: '${prefix}-vm1'
150    nicConfigurations: [
151      {
152        ipConfigurations: [
153          {
154            name: 'ipconfig01'
155            subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] // VMSubnet
156          }
157        ]
158        nicSuffix: '-nic-01'
159      }
160    ]
161    osDisk: {
162      caching: 'ReadWrite'
163      diskSizeGB: 128
164      managedDisk: {
165        storageAccountType: 'Standard_LRS'
166      }
167    }
168    osType: 'Linux'
169    vmSize: 'Standard_B2s_v2'
170    zone: 0
171    // Non-required parameters
172    location: location
173    managedIdentities: {
174      systemAssigned: true
175    }
176  }
177}
178
179module storageAccount 'br/public:avm/res/storage/storage-account:0.19.0' = {
180  name: 'storageAccountDeployment'
181  params: {
182    // Required parameters
183    name: '${uniqueString(resourceGroup().id)}sa'
184    // Non-required parameters
185    location: location
186    skuName: 'Standard_LRS'
187    diagnosticSettings: [
188      {
189        name: 'storageAccountDiagnostics'
190        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
191      }
192    ]
193    publicNetworkAccess: 'Disabled'
194    allowBlobPublicAccess: false
195    blobServices: {
196      containers: [
197        {
198          name: 'vmstorage'
199          publicAccess: 'None'
200        }
201      ]
202      roleAssignments:[
203        {
204          principalId: virtualMachine.outputs.systemAssignedMIPrincipalId
205          principalType: 'ServicePrincipal'
206          roleDefinitionName: 'Storage Blob Data Contributor' // Allows read/write/delete on blob containers
207        }
208      ]
209    }
210    privateEndpoints: [
211      {
212        service: 'Blob'
213        subnetResourceId: virtualNetwork.outputs.subnetResourceIds[1] // Private Endpoint Subnet
214        privateDnsZoneGroup: {
215          privateDnsZoneGroupConfigs: [
216            {
217              privateDnsZoneResourceId: privateDnsBlob.outputs.resourceId
218            }
219          ]
220        }
221      }
222    ]
223  }
224}
225
226module privateDnsBlob 'br/public:avm/res/network/private-dns-zone:0.7.1' = {
227  name: '${prefix}-privatedns-blob'
228  params: {
229    name: 'privatelink.blob.${environment().suffixes.storage}'
230    location: 'global'
231    virtualNetworkLinks: [
232      {
233        name: '${virtualNetwork.outputs.name}-vnetlink'
234        virtualNetworkResourceId: virtualNetwork.outputs.resourceId
235      }
236    ]
237  }
238}
239
240// Note: Deploying a Bastion Host will automatically create a Public IP and use the subnet named "AzureBastionSubnet"
241// within our VNet. This subnet is required and must be named exactly "AzureBastionSubnet" for the Bastion Host to work.
242module bastion 'br/public:avm/res/network/bastion-host:0.6.1' = {
243  name: 'bastionDeployment'
244  params: {
245    name: '${prefix}-bastion'
246    virtualNetworkResourceId: virtualNetwork.outputs.resourceId
247    skuName: 'Basic'
248    location: location
249    diagnosticSettings: [
250      {
251        name: 'bastionDiagnostics'
252        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
253      }
254    ]
255  }
256}
Info

The Azure Subscription owner will have CRUD permissions for the Storage Account but not for the Key Vault. The Key Vault requires explicit RBAC permissions assigned to a user to grant them access: Provide access to Key Vaults using RBAC. Important!: at this point, you will only be able to access the Storage Account from the Bastion Host. Remember, public internet access has been disabled!

The RBAC policies have been successfully applied using a System-assigned Managed Identity on the Virtual Machine. This identity has been granted permissions on both the Key Vault and Storage Account. Now the VM can read secrets from the Key Vault and Read, Create, or Delete blobs in the Storage Account.

In a real production environment, the principle of least privileged access should be applied, providing only the exact permissions each service needs to carry out its functions. Learn more about Microsoft’s recommendations for identity and access management.

Conclusion

In this tutorial, we’ve explored how to leverage Azure Verified Modules (AVM) to build a secure, well-architected solution in Azure. AVM modules significantly simplify the deployment of Azure resources by abstracting away much of the complexity involved in configuring individual resources.

Your final, deployable Bicep template file should now look like this:

➕ Expand Code
  1param location string = 'westus2'
  2
  3@description('Required. A password for the VM admin user.')
  4@secure()
  5param vmAdminPass string
  6
  7var addressPrefix = '10.0.0.0/16'
  8var prefix = 'VM-AVM-Ex1'
  9
 10module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.11.1' = {
 11  name: 'logAnalyticsWorkspace'
 12  params: {
 13    // Required parameters
 14    name: '${prefix}-law'
 15    // Non-required parameters
 16    location: location
 17  }
 18}
 19
 20module natGwPublicIp 'br/public:avm/res/network/public-ip-address:0.8.0' = {
 21  name: 'natGwPublicIpDeployment'
 22  params: {
 23    // Required parameters
 24    name: '${prefix}-natgwpip'
 25    // Non-required parameters
 26    location: location
 27    diagnosticSettings: [
 28      {
 29        name: 'natGwPublicIpDiagnostics'
 30        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 31      }
 32    ]
 33  }
 34}
 35
 36module natGateway 'br/public:avm/res/network/nat-gateway:1.2.2' = {
 37  name: 'natGatewayDeployment'
 38  params: {
 39    // Required parameters
 40    name: '${prefix}-natgw'
 41    zone: 1
 42    // Non-required parameters
 43    publicIpResourceIds: [
 44      natGwPublicIp.outputs.resourceId
 45    ]
 46  }
 47}
 48
 49module virtualNetwork 'br/public:avm/res/network/virtual-network:0.6.1' = {
 50  name: 'virtualNetworkDeployment'
 51  params: {
 52    // Required parameters
 53    addressPrefixes: [
 54      addressPrefix
 55    ]
 56    name: '${prefix}-vnet'
 57    // Non-required parameters
 58    location: location
 59    diagnosticSettings: [
 60      {
 61        name: 'vNetDiagnostics'
 62        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
 63      }
 64    ]
 65    subnets: [
 66      {
 67        name: 'VMSubnet'
 68        addressPrefix: cidrSubnet(addressPrefix, 24, 0) // first subnet in address space
 69        natGatewayResourceId: natGateway.outputs.resourceId
 70        networkSecurityGroupResourceId: nsgVM.outputs.resourceId
 71      }
 72      {
 73        name: 'PrivateEndpointSubnet'
 74        addressPrefix: cidrSubnet(addressPrefix, 24, 1) // second subnet in address space
 75      }
 76      {
 77        name: 'AzureBastionSubnet' // Azure Bastion Host requires this subnet to be named exactly "AzureBastionSubnet"
 78        addressPrefix: cidrSubnet(addressPrefix, 24, 2) // third subnet in address space
 79      }
 80    ]
 81  }
 82}
 83
 84module nsgVM 'br/public:avm/res/network/network-security-group:0.5.1' = {
 85  name: 'nsgVmDeployment'
 86  params: {
 87    name: '${prefix}-NSG-VM'
 88    location: location
 89    securityRules: [
 90      {
 91        name: 'AllowBastionSSH'
 92        properties: {
 93          access: 'Allow'
 94          direction: 'Inbound'
 95          priority: 100
 96          protocol: 'Tcp'
 97          sourceAddressPrefix: 'virtualNetwork'
 98          sourcePortRange: '*'
 99          destinationAddressPrefix: '*'
100          destinationPortRange: '22'
101        }
102      }
103    ]
104  }
105}
106
107module keyVault 'br/public:avm/res/key-vault/vault:0.12.1' = {
108  name: 'keyVaultDeployment'
109  params: {
110    // Required parameters
111    name: '${uniqueString(resourceGroup().id)}-kv'
112    // Non-required parameters
113    location: location
114    diagnosticSettings: [
115      {
116        name: 'keyVaultDiagnostics'
117        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
118      }
119    ]
120    enablePurgeProtection: false // disable purge protection for this example so we can more easily delete it
121    secrets: [
122      {
123        name: 'vmAdminPassword'
124        value: vmAdminPass
125      }
126    ]
127    roleAssignments: [
128      {
129        principalId: virtualMachine.outputs.systemAssignedMIPrincipalId
130        principalType: 'ServicePrincipal'
131        roleDefinitionIdOrName: 'Key Vault Secrets User' // Allows read access to secrets
132      }
133    ]
134  }
135}
136
137module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.14.0' = {
138  name: 'linuxVirtualMachineDeployment'
139  params: {
140    // Required parameters
141    adminUsername: 'localAdminUser'
142    adminPassword: vmAdminPass
143    imageReference: {
144      offer: '0001-com-ubuntu-server-jammy'
145      publisher: 'Canonical'
146      sku: '22_04-lts-gen2'
147      version: 'latest'
148    }
149    name: '${prefix}-vm1'
150    nicConfigurations: [
151      {
152        ipConfigurations: [
153          {
154            name: 'ipconfig01'
155            subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] // VMSubnet
156          }
157        ]
158        nicSuffix: '-nic-01'
159      }
160    ]
161    osDisk: {
162      caching: 'ReadWrite'
163      diskSizeGB: 128
164      managedDisk: {
165        storageAccountType: 'Standard_LRS'
166      }
167    }
168    osType: 'Linux'
169    vmSize: 'Standard_B2s_v2'
170    zone: 0
171    // Non-required parameters
172    location: location
173    managedIdentities: {
174      systemAssigned: true
175    }
176  }
177}
178
179module storageAccount 'br/public:avm/res/storage/storage-account:0.19.0' = {
180  name: 'storageAccountDeployment'
181  params: {
182    // Required parameters
183    name: '${uniqueString(resourceGroup().id)}sa'
184    // Non-required parameters
185    location: location
186    skuName: 'Standard_LRS'
187    diagnosticSettings: [
188      {
189        name: 'storageAccountDiagnostics'
190        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
191      }
192    ]
193    publicNetworkAccess: 'Disabled'
194    allowBlobPublicAccess: false
195    blobServices: {
196      containers: [
197        {
198          name: 'vmstorage'
199          publicAccess: 'None'
200        }
201      ]
202      roleAssignments:[
203        {
204          principalId: virtualMachine.outputs.systemAssignedMIPrincipalId
205          principalType: 'ServicePrincipal'
206          roleDefinitionName: 'Storage Blob Data Contributor' // Allows read/write/delete on blob containers
207        }
208      ]
209    }
210    privateEndpoints: [
211      {
212        service: 'Blob'
213        subnetResourceId: virtualNetwork.outputs.subnetResourceIds[1] // Private Endpoint Subnet
214        privateDnsZoneGroup: {
215          privateDnsZoneGroupConfigs: [
216            {
217              privateDnsZoneResourceId: privateDnsBlob.outputs.resourceId
218            }
219          ]
220        }
221      }
222    ]
223  }
224}
225
226module privateDnsBlob 'br/public:avm/res/network/private-dns-zone:0.7.1' = {
227  name: '${prefix}-privatedns-blob'
228  params: {
229    name: 'privatelink.blob.${environment().suffixes.storage}'
230    location: 'global'
231    virtualNetworkLinks: [
232      {
233        name: '${virtualNetwork.outputs.name}-vnetlink'
234        virtualNetworkResourceId: virtualNetwork.outputs.resourceId
235      }
236    ]
237  }
238}
239
240// Note: Deploying a Bastion Host will automatically create a Public IP and use the subnet named "AzureBastionSubnet"
241// within our VNet. This subnet is required and must be named exactly "AzureBastionSubnet" for the Bastion Host to work.
242module bastion 'br/public:avm/res/network/bastion-host:0.6.1' = {
243  name: 'bastionDeployment'
244  params: {
245    name: '${prefix}-bastion'
246    virtualNetworkResourceId: virtualNetwork.outputs.resourceId
247    skuName: 'Basic'
248    location: location
249    diagnosticSettings: [
250      {
251        name: 'bastionDiagnostics'
252        workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
253      }
254    ]
255  }
256}

AVM modules provide several key advantages over writing raw Bicep templates:

  1. Simplified Resource Configuration: AVM modules handle much of the complex configuration work behind the scenes
  2. Built-in Recommended Practices: The modules implement many of Microsoft’s recommended practices by default
  3. Consistent Outputs: Each module exposes a consistent set of outputs that can be easily referenced
  4. Reduced Boilerplate Code: What would normally require hundreds of lines of Bicep code can be accomplished in a fraction of the space

As you continue your journey with Azure and AVM, remember that this approach can be applied to more complex architectures as well. The modular nature of AVM allows you to mix and match components to build solutions that meet your specific needs while adhering to Microsoft’s Well-Architected Framework.

By using AVM modules as building blocks, you can focus more on your solution architecture and less on the intricacies of individual resource configurations, ultimately leading to faster development cycles and more reliable deployments.

Clean up your environment

When you are ready, you can remove the infrastructure deployed in this example. Key Vaults are set to a soft-delete state so you will also need to purge the one we created in order to fully delete it. The following commands will remove all resources created by your deployment:

Clean up with
# Delete the resource group
Remove-AzResourceGroup -Name "avm-bicep-vmexample1" -Force

# Purge the Key Vault
Remove-AzKeyVault -VaultName "<keyVaultName>" -Location "<location>" -InRemovedState -Force
# Delete the resource group
az group delete --name 'avm-bicep-vmexample1' --yes --no-wait

# Purge the Key Vault
az keyvault purge --name '<keyVaultName>' --no-wait

Congratulations, you have successfully leveraged AVM Bicep modules to deploy resources in Azure!

Tip

We welcome your contributions and feedback to help us improve the AVM modules and the overall experience for the community!

Terraform - Solution Development

Introduction

Azure Verified Modules (AVM) for Terraform are a powerful tool that leverage the Terraform domain-specific language (DSL), industry knowledge, and an Open Source community, which altogether enable developers to quickly deploy Azure resources that follow Microsoft’s recommended practices for Azure.
In this article, we will walk through the Terraform specific considerations and recommended practices on developing your solution leveraging Azure Verified Modules. We’ll review some of the design features and trade-offs and include sample code to illustrate each discussion point.

Prerequisites

You will need the following tools and components to complete this guide:

Before you begin, ensure you have these tools installed in your development environment.

Planning

Good module development should start with a good plan. Let’s first review the architecture and module design prior to developing our solution.

Solution Architecture

Before we begin coding, it is important to have details about what the infrastructure architecture will include. For our example, we will be building a solution that will host a simple application on a Linux virtual machine (VM).

In our design, the resource group for our solution will require appropriate tagging to comply with our corporate standards. Resources that support Diagnostic Settings must also send metric data to a Log Analytics workspace, so that the infrastructure support teams can get metric telemetry. The virtual machine will require outbound internet access to allow the application to properly function. A Key Vault will be included to store any secrets and key artifacts, and we will include a Bastion instance to allow support personnel to access the virtual machine if needed. Finally, the VM is intended to run without interaction, so we will auto-generate an SSH private key and store it in the Key Vault for the rare event of someone needing to log into the VM.

Based on this narrative, we will create the following resources:

  • A resource group to contain all the resources with tagging
  • A random string resource for use in resources with global naming (Key Vault)
  • A Log Analytics workspace for diagnostic data
  • A Key Vault with:
    • Role-Based Access Control (RBAC) to allow data access
    • Logging to the Log Analytics workspace
  • A virtual network with:
    • A virtual machine subnet
    • A Bastion subnet
    • Network Security Group on the VM subnet allowing SSH traffic
    • Logging to the Log Analytics workspace
  • A NAT Gateway for enabling outbound internet access
    • Associated to the virtual machine subnet
  • A Bastion service for secure remote access to the Virtual Machine
    • Logging to the Log Analytics workspace
  • A virtual machine resource with
    • A single private IPv4 interface attached to the VM subnet
    • A randomly generated admin account private key stored in the Key Vault
    • Metrics sent to the log Analytics workspace
Azure VM Solution Architecture

Solution template (root module) design

Since our solution template (root module) is intended to be deployed multiple times, we want to develop it in a way that provides flexibility while minimizing the amount of input necessary to deploy the solution. For these reasons, we will create our module with a small set of variables that allow for deployment differentiation while still populating solution-specific defaults to minimize input. We will also separate our content into variables.tf, outputs.tf, terraform.tf, and main.tf files to simplify future maintenance.

Based on this, our file system will take the following structure:

  • Module Directory
    • terraform.tf - This file holds the provider definitions and versions.
    • variables.tf - This file contains the input variable definitions and defaults.
    • outputs.tf - This file contains the outputs and their descriptions for use by any external modules calling this root module.
    • main.tf - This file contains the core module code for creating the solutions infrastructure.
    • development.tfvars - This file will contain the inputs for the instance of the module that is being deployed. Content in this file will vary from instance to instance.
Note

Terraform will merge content from any file ending in a .tf extension in the module folder to create the full module content. Because of this, using different files is not required. We encourage file separation to allow for organizing code in a way that makes it easier to maintain. While the naming structure we’ve used is common, there are many other valid file naming and organization options that can be used.

In our example, we will use the following variables as inputs to allow for customization:

  • location - The location where our infrastructure will be deployed.
  • name_prefix - This will be used to preface all of the resource naming.
  • virtual_network_prefix - This will be used to ensure IP uniqueness for the deployment.
  • tags - The custom tags to use for each deployment.

Finally, we will export the following outputs:

  • resource_group_name - This will allow for finding this deployment if there are multiples.
  • virtual_machine_name - This can be used to find and login to the vm if needed.

Identifying AVM modules that match our solution

Now that we’ve determined our architecture and module configurations, we need to see what AVM modules exist for use in our solution. To do this, we will open the AVM Terraform pattern module index and check if there are any existing pattern modules that match our requirement. In this case, no pattern modules fit our needs. If this was a common pattern, we could open an issue on the AVM github repository to get assistance from the AVM project to create a pattern module matching our requirements. Since our architecture isn’t common, we’ll continue to the next step.

When a pattern module fitting our needs doesn’t exist for a solution, leveraging AVM resource modules to build our own solution is the next best option. Review the AVM Terraform published resource module index for each of the resource types included in your architecture. For each AVM module, capture a link to the module to allow for a review of the documentation details on the Terraform Registry website.

Note

Some of the published pattern modules cover multi-resource configurations that can sometimes be interpreted as a single resource. Be sure to check the pattern index for groups of resources that may be part of your architecture and that don’t exist in the resource module index. (e.g., Virtual WAN)

For our sample architecture, we have the following AVM resource modules at our disposal. Click on each module to explore its documentation on the Terraform Registry.

Develop the Solution Code

We can now begin coding our solution. We will create each element individually, to allow us to test our deployment as we build it out. This will also allow us to correct any bugs incrementally, so that we aren’t troubleshooting a large number of resources at the end.

Creating the terraform.tf file

Let’s begin by configuring the provider details necessary to build our solution. Since this is a root module, we want to include any provider and Terraform version constraints for this module. We’ll periodically come back and add any needed additional providers if our design includes a resource from a new provider.

Open up your development IDE (Visual studio code in our example) and create a file named terraform.tf in your root directory.

Add the following code to your terraform.tf file:

➕ Expand Code
1terraform {
2  required_version = "~> 1.9"
3  required_providers {
4  }
5}
Note

Always click on the “Copy to clipboard” button in the top right corner of the Code sample area in order not to have the line numbers included in the copied code.

This specifies that the required Terraform binary version to run your module can be any version between 1.9 and 2.0. This is a good compromise for allowing a range of binary versions while also ensuring support for any required features that are used as part of the module. This can include things like newly introduced functions or support for new key words.

Since we are developing our solution incrementally, we should validate our code. To do this, we will take the following steps:

  1. Open up a terminal window if it is not already open. In some IDE’s this can be done as a function of the IDE.
  2. Change directory to the module directory by typing cd and then the path to the module. As an example, if the module directory was named example we would run cd example.
  3. Run terraform init to initialize your provider file.

You should now see a message indicating that Terraform has been successfully initialized. This indicates that our code is error free and we can continue on. If you get errors, examine the provider syntax for typos, missing quotes, or missing brackets.

Creating a variables.tf file

Because our module is intended to be reusable, we want to provide the capability to customize each module call with those items that will differ between them. This is done by using variables to accept inputs into the module. We’ll define these inputs in a separate file named variables.tf.

Go back to the IDE, and create a file named variables.tf in the working directory.

Add the following code to your variables.tf file to configure the inputs for our example:

➕ Expand Code
 1variable "name_prefix" {
 2  description = "Prefix for the name of the resources"
 3  type        = string
 4  default     = "example"
 5}
 6
 7variable "location" {
 8  description = "The Azure location to deploy the resources"
 9  type        = string
10  default     = "East US"
11}
12
13variable "virtual_network_cidr" {
14  description = "The CIDR prefix for the virtual network. This should be at least a /22. Example 10.0.0.0/22"
15  type        = string
16}
17
18variable "tags" {
19  description = "Tags to be applied to all resources"
20  type        = map(string)
21  default     = {}
22}
Note

Note that each variable definition includes a type definition to guide module users on how to properly define an input. Also note that it is possible to set a default value. This allows module consumers to avoid setting a value if they find the default to be acceptable.

We should now test the new content we’ve created for our module. To do this, first re-run terraform init on your command line. Note that nothing has changed and the initialization completes successfully. Since we now have module content, we will attempt to run the plan as the next step of the workflow.

Type terraform plan on your command line. Note that it now asks for us to provide a value for the var.virtual_network_cidr variable. This is because we don’t provide a default value for that input so Terraform must have a valid input before it can continue. Type 10.0.0.0/22 into the input and press enter to allow the plan to complete. You should now see a message indicating that Your infrastructure matches the configuration and that no changes are needed.

Creating a development.tfvars file

There are multiple ways to provide input to the module we’re creating. We will create a tfvars file that can be supplied during plan and apply stages to minimize the need for manual input. tfvars files are a nice way to document inputs as well as allow for deploying different versions of your module. This is useful if you have a pipeline where infrastructure code is deployed first for development, and then is deployed for QA, staging, or production with different input values.

In your IDE, create a new file named development.tfvars in your working directory.

Now add the following content to your development.tfvars file.

➕ Expand Code
1location = "westus2"
2prefix = dev
3virtual_network_cidr = "10.1.0.0/22"
4tags = {
5  environment = "development"
6  owner       = "dev-team"
7}
Note

Note that each variable has a value defined. Although, only inputs without default values are required, we include values for all of the inputs for clarity. Consider doing this in your environments so that someone looking at the tfvars files has a full picture of what values are being set.

Re-run the terraform apply, but this time, reference the .tfvars file by using the following command: terraform plan -var-file=development.tfvars. You should get a successful completion without needing to manually provide inputs.

Creating the main.tf file

Now that we’ve created the supporting files, we can start building the actual infrastructure code in our main file. We will add one AVM resource module at a time so that we can test each as we implement them.

Return to your IDE and create a new file named main.tf.

Add a resource group

In Azure, we need a resource group to hold any infrastructure resources we create. This is a simple resource that typically wouldn’t require an AVM module, but we’ll include the AVM module so we can take advantage of the Role-Based Access Control (RBAC) interface if we need to restrict access to the resource group in future versions.

First, let’s visit the Terraform registry documentation page for the resource group and explore several key sections.

  1. Note the Provision Instructions box on the right-hand side of the page. This contains the module source and version details which allows us to copy the latest version syntax without needing to type everything ourselves.
  2. Now review the Readme tab in the middle of the page. It contains details about all required and optional inputs, resources that are created with the module, and any outputs that are defined. If you want to explore any of these items in detail, each element has a tab that you can review as needed.
  3. Finally, in the middle of the page, there is a drop-down menu named Examples that contains functioning examples for the AVM module. These showcase a good example of using copy/paste to bootstrap module code and then modify it for your specific purpose.

Now that we’ve explored the registry content, let’s add a resource group to our module.

First, copy the content from the Provision Instructions box into our main.tf file.

➕ Expand Code
1module "avm-res-resources-resourcegroup" {
2  source  = "Azure/avm-res-resources-resourcegroup/azurerm"
3  version = "0.2.1"
4  # insert the 2 required variables here
5}

On the modules documentation page, go to the inputs tab. Review the Required Inputs tab. These are the values that don’t have defaults and are the minimum required values to deploy the module. There are additional inputs in the Optional Inputs section that can be used to configure additional module functionality. Review these inputs and determine which values you would like to define in your AVM module call.

Now, replace the # insert the 2 required variables here comment with the following code to define the module inputs. Our main.tf code should look like the following:

➕ Expand Code
1module "avm-res-resources-resourcegroup" {
2  source  = "Azure/avm-res-resources-resourcegroup/azurerm"
3  version = "0.2.1"
4
5  name = "${var.name_prefix}-rg"
6  location = var.location
7  tags = var.tags
8}
Note

Note how we’ve used the prefix variable and Terraform interpolation syntax to dynamically name the resource group. This allows for module customization and re-use. Also note that even though we chose to use the default module name of avm-res-resources-resourcegroup, we could modify the name of the module if needed.

After saving the file, we want to test our new content. To do this, return to the command line and first run terraform init. Notice how Terraform has downloaded the module code, as well as providers that the module requires. In this case, you can see the azurerm, random, and modtm providers were downloaded.

Let’s now deploy our resource group. First, let’s run a plan operation to review what will be created. Type terraform plan -var-file=development.tfvars and press enter to initiate the plan.

Add the features block

Notice that we get an error indicating that we are Missing required argument and that for the azurerm provider, we need to provide a features argument. The addition of the resource group AVM resource requires that the azurerm provider be installed to provision resources in our module. This provider requires a features block in its provider definition that is missing in our configuration.

Return to the terraform.tf file and add the following content to it. Note how the features block is currently empty. If we needed to activate any feature flags in our module, we could add them here.

➕ Expand Code
 1terraform {
 2  required_version = "~> 1.9"
 3  required_providers {
 4  }
 5}
 6
 7provider "azurerm" {
 8  features {
 9  }
10}

Re-run terraform plan -var-file=development.tfvars now that we have updated the features block.

Set the subscription ID

Note that we once again get an error. This time, the error indicates that subscription_id is a required provider property for plan/apply operations. This is a change that was introduced as part of the version 4 release of the AzureRM provider. We need to supply the ID of the deployment subscription where our resources will be created.

First, we need to get the subscription ID value. We will use the portal for this exercise, but using the Azure CLI, PowerShell, or the resource graph will also work to retrieve this value.

  1. Open the Azure portal.
  2. Enter Subscriptions in the search field at the top middle of the page.
  3. Select Subscriptions from the services menu in the search drop-down.
  4. Select the subscription you wish to deploy to, from the list of subscriptions.
  5. Find the Subscription ID field on the overview page and click the copy button to copy it to the clipboard.

Secondly, we need to update Terraform so that it can use the subscription ID. There are multiple ways to provide a subscription ID to the provider including adding it to the features block or using environment variables. For this scenario we’ll use environment variables to set the values so that we don’t have to re-enter them on each run. This also keeps us from storing the subscription ID in our code since it is considered a sensitive value. Select a command from the list below based on your operating system.

  1. (Linux/MacOS) - Run the following command with your subscription ID: export ARM_SUBSCRIPTION_ID=<your ID here>
  2. (Windows) - Run the following command with your subscription ID: set ARM_SUBSCRIPTION_ID=<your ID here>

Finally, we should now be able to complete our plan operation by re-running terraform plan -var-file=development.tfvars. Note that the plan will create three resources, two for telemetry and one for the resource group.

Deploy the resource group

We can complete testing by implementing the resource group. Run terraform apply -var-file="development.tfvars" and type yes and press enter when prompted to accept the changes. Terraform will create the resource group and notify you with a Apply complete message and a summary of the resources that were added, changed, and destroyed.

Deploy the Log Analytics Workspace

We can now continue by adding the Log Analytics Workspace to our main.tf file. We will follow a workflow similar to what we did with the resource group.

  1. Browse to the AVM Log Analytics Workspace module page in the Terraform Registry.
  2. Copy the module content from the Provision Instructions portion of the page into the main.tf file.

This time, instead of manually supplying module inputs, we will copy module content from one of the examples to minimize the amount of typing required. In most examples, the AVM module call is located at the bottom of the example.

  1. Navigate to the Examples drop-down menu in the documentation and select the default example from the menu. You will see a fully functioning example code which includes the module and any supporting resources. Since we only care about the workspace resource from this example, we can scroll to the bottom of the code block and find the module "log_analytics_workspace" line.
  2. Copy the content between the module brackets with the exception of the line defining the module source. Because these examples are part of the testing methodology for the module, they use a dot reference value (../..) for the module source value which will not work in our module call. To work around this, we copied those values from the provision instructions section of the module documentation in a previous step.
  3. Update the location and resource group name values to reference outputs from the resource group module. Using implicit references such as these allow Terraform to determine the order in which resources should be built.
  4. Update the name field using the prefix variable to allow for customization using a similar pattern to what we used on the resource group.

The Log Analytics module content should look like the following code block. For simplicity, you can also copy this directly to avoid multiple copy/paste actions.

➕ Expand Code
 1module "avm-res-operationalinsights-workspace" {
 2  source  = "Azure/avm-res-operationalinsights-workspace/azurerm"
 3  version = "0.4.2"
 4
 5  enable_telemetry                          = true
 6  location                                  = module.avm-res-resources-resourcegroup.resource.location
 7  resource_group_name                       = module.avm-res-resources-resourcegroup.name
 8  name                                      = "${var.name_prefix}-law"
 9  log_analytics_workspace_retention_in_days = 30
10  log_analytics_workspace_sku               = "PerGB2018"
11}

Again, we will need to run terraform init to allow Terraform to initialize a copy of the AVM Log Analytics module.

Now, we can deploy the Log Analytics workspace by running terraform apply -var-file="development.tfvars", typing yes and pressing enter. Note that Terraform will only create the new Log Analytics resources since the resource group already exists. This is one of the key benefits of deploying using Infrastructure as Code (IAC) tools like Terraform.

Note

Note that we ran the terraform apply command without first running terraform plan. Because terraform apply runs a plan before prompting for the apply, we opted to shorten the instructions by skipping the explicit plan step. If you are testing in a live environment, you may want to run the plan step and save the plan as part of your governance or change control processes.

Deploy the Azure Key Vault

Our solution calls for a simple Key Vault implementation to store virtual machine secrets. We’ll follow the same workflow for deploying the Key Vault as we used for the previous resource group and Log Analytics workspace resources. However, since Key Vaults require data roles to manage secrets and keys, we will need to use the RBAC interface and a data resource to configure Role-Based Access Control (RBAC) during the deployment.

Note

For this exercise, we will provision the deployment user with data rights on the Key Vault. In your environment, you will likely want to either provide additional roles as inputs or statically assign users, or groups to the Key Vault data roles. For simplicity we also set the Key Vault to have public access enabled due to us not being able to dictate a private deployment environment. In your environment where your deployment machine will be on a private network it is recommended to restrict public access for the Key Vault.

Before we implement the AVM module for the Key Vault, we want to use a data resource to read the client details about the user context of the current Terraform deployment.

Add the following line to your main.tf file and save it.

➕ Expand Code
1data "azurerm_client_config" "this" {}

Key vaults use a global namespace which means that we will also need to add a randomization resource to allow us to randomize the name to avoid any potential name intersection issues with other Key Vault deployments. We will use Terraform’s random provider to generate the random string which we will append to the Key Vault name. Add the following code to your main module to create the random_string resource we will use for naming.

➕ Expand Code
1resource "random_string" "name_suffix" {
2  length  = 4
3  special = false
4  upper   = false
5}

Now we can continue with adding the AVM Key Vault module to our solution.

  1. Browse to the AVM Key Vault resource module page in the Terraform Registry.
  2. Copy the module content from the Provision Instructions portion of the page into the main.tf file.
  3. This time, we’re going to select relevant content from the Create secret example to fill out our module.
  4. Copy the name, location, enable_telemetry, resource_group_name, tenant_id, and role_assignments value content from the example and paste it into the new Key Vault module in your solution.
  5. Update the name value to be "${var.prefix}-kv-${random_string.name_suffix.result}"
  6. Update the location and resource_group_name values to the same implicit resource group module references we used in the Log Analytics workspace.
  7. Set the enable_telemetry value to true.
  8. Leave the tenant_id and role_assignments values to the same values that are in the example.

Our architecture calls for us to include a diagnostic settings configuration for each resource that supports it. We’ll use the diagnostic-settings example to copy this content.

  1. Return to the documentation page and select the diagnostic-settings option from the examples drop-down.
  2. Locate the Key Vault resource in the example’s code block and copy the diagnostic_settings value and paste it into the Key Vault module block we’re building in main.tf.
  3. Update the name value to use our prefix variable to allow for name customization.
  4. Update the workspace_resource_id value to be an implicit reference to the output from the previously implemented Log Analytics module (module.avm-res-operationalinsights-workspace.resource_id in our code).

Finally, we will allow public access, so that our deployer machine can add secrets to the Key Vault. If your environment doesn’t allow public access for Key Vault deployments, locate the public IP address of your deployer machine (this may be an external NAT IP for your network) and add it to the network_acls.ip_rules list value using CIDR notation.

  1. Set the network_acls input to null in your module block for the Key Vault.

Your Key Vault module definition should now look like the following:

➕ Expand Code
 1module "avm-res-keyvault-vault" {
 2  source  = "Azure/avm-res-keyvault-vault/azurerm"
 3  version = "0.10.0"
 4
 5  enable_telemetry    = true
 6  location            = module.avm-res-resources-resourcegroup.resource.location
 7  resource_group_name = module.avm-res-resources-resourcegroup.name
 8  name                = "${var.name_prefix}-kv-${random_string.name_suffix.result}"
 9  tenant_id           = data.azurerm_client_config.this.tenant_id
10  network_acls        = null
11
12  diagnostic_settings = {
13    to_la = {
14      name                  = "${var.name_prefix}-kv-diags"
15      workspace_resource_id = module.avm-res-operationalinsights-workspace.resource_id
16    }
17  }
18
19  role_assignments = {
20    deployment_user_kv_admin = {
21      role_definition_id_or_name = "Key Vault Administrator"
22      principal_id               = data.azurerm_client_config.this.object_id
23    }
24  }
25}
Note

One of the core values of AVM is the standard configuration for interfaces across modules. The Role Assignments interface we used as part of the Key Vault deployment is a good example of this.

Continue the incremental testing of your module by running another terraform init and terraform apply -var-file="development.tfvars" sequence.

Deploy the NAT Gateway

Our architecture calls for a NAT Gateway to allow virtual machines to access the internet. We will use the NAT Gateway resource_id output in future modules to link the virtual machine subnet.

  1. Browse to the AVM NAT Gateway resource module page in the Terraform Registry.
  2. Copy the module definition and source from the Provision Instructions card from the module main page.
  3. Copy the remaining module content from the default example excluding the subnet associations map, as we will do the association when we build the vnet.
  4. Update the location and resource_group_nameusing implicit references from our resource group module.
  5. Then update each of the name values to use the name_prefix variables.

Review the following code to see each of these changes.

➕ Expand Code
 1module "avm-res-network-natgateway" {
 2  source  = "Azure/avm-res-network-natgateway/azurerm"
 3  version = "0.2.1"
 4
 5  name                = "${var.name_prefix}-natgw"
 6  enable_telemetry    = true
 7  location            = module.avm-res-resources-resourcegroup.resource.location
 8  resource_group_name = module.avm-res-resources-resourcegroup.name
 9
10  public_ips = {
11    public_ip_1 = {
12      name = "${var.name_prefix}-natgw-pip"
13    }
14  }
15}

Continue the incremental testing of your module by running another terraform init and terraform apply -var-file="development.tfvars" sequence.

Deploy the Network Security Group

Our architecture calls for a Network Security Group (NSG) allowing SSH access to the virtual machine subnet. We will use the NSG AVM resource module to accomplish this task.

  1. Browse to the AVM Network Security Group resource module page in the Terraform Registry.
  2. Copy the module definition and source from the Provision Instructions card from the module main page.
  3. Copy the remaining module content from the example_with_NSG_rule example.
  4. Update the location and resource_group_nameusing implicit references from our resource group module.
  5. Update the name value using the name_prefix variable interpolation as we did with the other modules.
  6. Copy the map entry labeled rule02 from the locals nsg_rules map and paste it between two curly braces to create the security_rules attribute in the NSG module we’re building.
  7. Make the following updates to the rule details:
    1. Rename the map key to "rule01" from "rule02".
    2. Update the name to use the var.prefix interpolation and SSH to describe the rule.
    3. Update the destination_port_ranges list to be ["22"].

Upon completion the code for the NSG module should be as follows:

➕ Expand Code
 1module "avm-res-network-networksecuritygroup" {
 2  source  = "Azure/avm-res-network-networksecuritygroup/azurerm"
 3  version = "0.4.0"
 4  resource_group_name = module.avm-res-resources-resourcegroup.name
 5  name                = "${var.name_prefix}-vm-subnet-nsg"
 6  location            = module.avm-res-resources-resourcegroup.resource.location
 7
 8  security_rules = {
 9    "rule01" = {
10      name                       = "${var.name_prefix}-ssh"
11      access                     = "Allow"
12      destination_address_prefix = "*"
13      destination_port_ranges    = ["22"]
14      direction                  = "Inbound"
15      priority                   = 200
16      protocol                   = "Tcp"
17      source_address_prefix      = "*"
18      source_port_range          = "*"
19    }
20  }
21}

Continue the incremental testing of your module by running another terraform init and terraform apply -var-file="development.tfvars" sequence.

Deploy the Virtual Network

We can now continue the build-out of our architecture by configuring the virtual network (vnet) deployment. This will follow a similar pattern as the previous resource modules, but this time, we will also add some network functions to help us customize the subnet configurations.

  1. Browse to the AVM Virtual Network resource module page in the Terraform Registry.
  2. Copy the module definition and source from the Provision Instructions card from the module main page.
  3. After looking through the examples, this time, we’ll use the complete example as a source to copy our content.
  4. Copy the resource_group_name, location, name, and address_space lines and replace their values with our deployment specific variables or module references.
  5. We’ll copy the subnets map and duplicate the subnet0 map for each subnet.
  6. Now we will update the map key and name values for each subnet so that they are unique.
  7. Then we’ll use the cidrsubnet function to dynamically generate the CIDR range for each subnet. You can explore the function documentation for more details on how it can be used.
  8. We will also populate the nat_gateway object on subnet0 with the resource_id output from our NAT Gateway module.
  9. To configure the NSG on the VM subnet we need to link it. Add a network_security_group attribute to the subnet0 definition and replace the value with the resource_id output from the NSG module.
  10. Finally, we’ll copy the diagnostic settings from the example and update the implicit references to point to our previously deployed Log Analytics workspace.

After making these changes our virtual network module call code will be as follows:

➕ Expand Code
 1module "avm-res-network-virtualnetwork" {
 2  source  = "Azure/avm-res-network-virtualnetwork/azurerm"
 3  version = "0.8.1"
 4
 5  resource_group_name = module.avm-res-resources-resourcegroup.name
 6  location            = module.avm-res-resources-resourcegroup.resource.location
 7  name                = "${var.name_prefix}-vnet"
 8
 9  address_space = [var.virtual_network_cidr]
10
11  subnets = {
12    subnet0 = {
13      name                            = "${var.name_prefix}-vm-subnet"
14      default_outbound_access_enabled = false
15      address_prefixes = [cidrsubnet(var.virtual_network_cidr, 1, 0)]
16      nat_gateway = {
17        id = module.avm-res-network-natgateway.resource_id
18      }
19      network_security_group = {
20        id = module.avm-res-network-networksecuritygroup.resource_id
21      }
22    }
23    bastion = {
24      name                            = "AzureBastionSubnet"
25      default_outbound_access_enabled = false
26      address_prefixes = [cidrsubnet(var.virtual_network_cidr, 1, 1)]
27    }
28  }
29
30  diagnostic_settings = {
31    sendToLogAnalytics = {
32      name                           = "${var.name_prefix}-vnet-diagnostic"
33      workspace_resource_id          = module.avm-res-operationalinsights-workspace.resource_id
34      log_analytics_destination_type = "Dedicated"
35    }
36  }
37}
Note

Note how the Log Analytics workspace reference ends in resource_id. Each AVM module is required to export its Azure resource ID with the resource_id name to allow for consistent references.

Continue the incremental testing of your module by running another terraform init and terraform apply -var-file="development.tfvars" sequence.

Deploy the Bastion service

We want to allow for secure remote access to the virtual machine for configuration and troubleshooting tasks. We’ll use Azure Bastion to accomplish this objective following a similar workflow to our other resources.

  1. Browse to the AVM Bastion resource module page in the Terraform Registry.
  2. Copy the module definition and source from the Provision Instructions card from the module main page.
  3. Copy the remaining module content from the Simple Deployment example.
  4. Update the location and resource_group_nameusing implicit references from our resource group module.
  5. Update the name value using the name_prefix variable interpolation as we did with the other modules.
  6. Finally, update the subnet_id value to include an implicit reference to the bastion keyed subnet from our virtual network module.

Our architecture calls for diagnostic settings to be configured on the Azure Bastion resource. In this case, there aren’t any examples that include this configuration. However, since the diagnostic settings interface is one of the standard interfaces in Azure Verified Modules, we can just copy the interface definition from our virtual network module.

  1. Locate the virtual network module and copy the diagnostic_settings value from it.
  2. Paste the diagnostic_settings value into the code for our Bastion module.
  3. Update the diagnostic setting’s name value from vnet to Bastion.

The new code we added for the Bastion resource will be as follows:

➕ Expand Code
 1module "avm-res-network-bastionhost" {
 2  source  = "Azure/avm-res-network-bastionhost/azurerm"
 3  version = "0.7.2"
 4
 5  name                = "${var.name_prefix}-bastion"
 6  resource_group_name = module.avm-res-resources-resourcegroup.name
 7  location            = module.avm-res-resources-resourcegroup.resource.location
 8  ip_configuration = {
 9    subnet_id = module.avm-res-network-virtualnetwork.subnets["bastion"].resource_id
10  }
11
12  diagnostic_settings = {
13    sendToLogAnalytics = {
14      name                           = "${var.name_prefix}-bastion-diagnostic"
15      workspace_resource_id          = module.avm-res-operationalinsights-workspace.resource_id
16      log_analytics_destination_type = "Dedicated"
17    }
18  }
19}
Note

Pay attention to the subnet_id syntax. In the virtual network module, the subnets are created as a sub-module allowing us to reference each of them using the map key that was defined in the subnets input. Again, we see the consistent output naming with the resource_id output for the sub-module.

Continue the incremental testing of your module by running another terraform init and terraform apply -var-file="development.tfvars" sequence.

Deploy the virtual machine

The final step in our deployment will be our application virtual machine. We’ve had good success with our workflow so far, so we’ll use it for this step as well.

  1. Browse to the AVM Virtual Machine resource module page in the Terraform Registry.
  2. Copy the module definition and source from the Provision Instructions card from the module main page.
  3. Copy the remaining module content from the linux_default example.
  4. Update the location and resource_group_nameusing implicit references from our resource group module.
  5. To be compliant with Well Architected Framework guidance we encourage defining a zone if your region supports it. Update the zone input to 1.
  6. Update the sku_size input to “Standard_D2s_v5”.
  7. Update the name values using the name_prefix variable interpolation as we did with the other modules and include the output from the random_string.name_suffix resource to add uniqueness.
  8. Set the account_credentials.key_vault_configuration.resource_id value to reference the resource_id output from the Key Vault module.
  9. Update the private_ip_subnet_resource_id value to an implicit reference to the subnet0 subnet output from the virtual network module.

Because the default Linux example doesn’t include diagnostic settings, we need to add that content in a different way. Since the diagnostic settings interface has a standard schema, we can copy the diagnostic_settings input from our virtual network module.

  1. Locate the virtual network module in your code and copy the diagnostic_settings map from it.
  2. Paste the diagnostic_settings content into your virtual machine module code.
  3. Update the name value to reflect that it applies to the virtual machine.

The new code we added for the virtual machine resource will be as follows:

➕ Expand Code
 1module "avm-res-compute-virtualmachine" {
 2  source  = "Azure/avm-res-compute-virtualmachine/azurerm"
 3  version = "0.19.1"
 4
 5  enable_telemetry    = true
 6  location            = module.avm-res-resources-resourcegroup.resource.location
 7  resource_group_name = module.avm-res-resources-resourcegroup.name
 8  name                = "${var.name_prefix}-vm"
 9  os_type             = "Linux"
10  sku_size            = "Standard_D2s_v5"
11  zone                = 1
12
13  source_image_reference = {
14    publisher = "Canonical"
15    offer     = "0001-com-ubuntu-server-focal"
16    sku       = "20_04-lts-gen2"
17    version   = "latest"
18  }
19
20  network_interfaces = {
21    network_interface_1 = {
22      name = "${var.name_prefix}-nic-${random_string.name_suffix.result}"
23      ip_configurations = {
24        ip_configuration_1 = {
25          name                          = "${var.name_prefix}-ipconfig-${random_string.name_suffix.result}"
26          private_ip_subnet_resource_id = module.avm-res-network-virtualnetwork.subnets["subnet0"].resource_id
27        }
28      }
29    }
30  }
31
32  diagnostic_settings = {
33    sendToLogAnalytics = {
34      name                           = "${var.name_prefix}-vm-diagnostic"
35      workspace_resource_id          = module.avm-res-operationalinsights-workspace.resource_id
36      log_analytics_destination_type = "Dedicated"
37    }
38  }
39}

Continue the incremental testing of your module by running another terraform init and terraform apply -var-file="development.tfvars" sequence.

Creating the outputs.tf file

The final piece of our module is to export any values that may need to be consumed by module users. From our architecture, we’ll export the resource group name and the virtual machine resource name.

  1. Create an outputs.tf file in your IDE.
  2. Create an output named resource_group_name and set the value to an implicit reference to the resource group modules name output. Include a brief description for the output.
  3. Create an output named virtual_machine_name and set the value to an implicit reference to the virtual machine module’s name output. Include a brief description for the output.

The new code we added for the outputs will be as follows:

➕ Expand Code
1output "resource_group_name" {
2  value =  module.avm-res-resources-resourcegroup.name
3  description = "The resource group name where the resources are deployed"
4}
5
6output "virtual_machine_name" {
7    value = module.avm-res-compute-virtualmachine.name
8    description = "The name of the virtual machine"
9}

Because no new modules were created, we don’t need to run terraform init to test this change. Run terraform apply -var-file="development.tfvars" to see the new outputs that have been created.

Update the terraform.tf file

It is a recommended practice to define the required versions of the providers for your module to ensure consistent behavior when it is being run. In this case we are going to be slightly permissive and allow increases in minor and patch versions to fluctuate, since those are not supposed to include breaking changes. In a production environment, you would likely want to pin on a specific version to guarantee behavior.

  1. Run terraform init to review the providers and versions that are currently installed.
  2. Update your terraform.tf file’s required providers field for each provider listed in the downloaded providers.

The updated code we added for the providers in the terraform.tf file will be as follows:

➕ Expand Code
 1terraform {
 2  required_version = "~> 1.9"
 3  required_providers {
 4    azapi = {
 5      source  = "azure/azapi"
 6      version = "~> 2.3"
 7    }
 8    azurerm = {
 9      source  = "hashicorp/azurerm"
10      version = "~> 4.27"
11    }
12    modtm = {
13      source  = "azure/modtm"
14      version = "~> 0.3"
15    }
16    random = {
17      source  = "hashicorp/random"
18      version = "~> 3.7"
19    }
20    time = {
21      source  = "hashicorp/time"
22      version = "~> 0.13"
23    }
24    tls = {
25      source  = "hashicorp/tls"
26      version = "~> 4.1"
27    }
28  }
29}
30
31provider "azurerm" {
32  features {
33  }
34}

Conclusion

Congratulations on successfully implementing a solution using Azure Verified Modules! You were able to build out our sample architecture using module documentation and taking advantage of features like standard interfaces and pre-defined defaults to simplify the development experience.

Note

This was a long exercise and mistakes can happen. If you’re getting errors or a resource is incomplete and you want to see the final main.tf, expand the following code block to see the full file.

➕ Expand Code
  1module "avm-res-resources-resourcegroup" {
  2  source  = "Azure/avm-res-resources-resourcegroup/azurerm"
  3  version = "0.2.1"
  4
  5  name = "${var.name_prefix}-rg"
  6  location = var.location
  7  tags = var.tags
  8}
  9
 10module "avm-res-operationalinsights-workspace" {
 11  source  = "Azure/avm-res-operationalinsights-workspace/azurerm"
 12  version = "0.4.2"
 13
 14  enable_telemetry                          = true
 15  location                                  = module.avm-res-resources-resourcegroup.resource.location
 16  resource_group_name                       = module.avm-res-resources-resourcegroup.name
 17  name                                      = "${var.name_prefix}-law"
 18  log_analytics_workspace_retention_in_days = 30
 19  log_analytics_workspace_sku               = "PerGB2018"
 20}
 21
 22data "azurerm_client_config" "this" {}
 23
 24resource "random_string" "name_suffix" {
 25  length  = 4
 26  special = false
 27  upper   = false
 28}
 29
 30module "avm-res-keyvault-vault" {
 31  source  = "Azure/avm-res-keyvault-vault/azurerm"
 32  version = "0.10.0"
 33
 34  enable_telemetry    = true
 35  location            = module.avm-res-resources-resourcegroup.resource.location
 36  resource_group_name = module.avm-res-resources-resourcegroup.name
 37  name                = "${var.name_prefix}-kv-${random_string.name_suffix.result}"
 38  tenant_id           = data.azurerm_client_config.this.tenant_id
 39  network_acls        = null
 40
 41  diagnostic_settings = {
 42    to_la = {
 43      name                  = "${var.name_prefix}-kv-diags"
 44      workspace_resource_id = module.avm-res-operationalinsights-workspace.resource_id
 45    }
 46  }
 47
 48  role_assignments = {
 49    deployment_user_kv_admin = {
 50      role_definition_id_or_name = "Key Vault Administrator"
 51      principal_id               = data.azurerm_client_config.this.object_id
 52    }
 53  }
 54}
 55
 56module "avm-res-network-natgateway" {
 57  source  = "Azure/avm-res-network-natgateway/azurerm"
 58  version = "0.2.1"
 59
 60  name                = "${var.name_prefix}-natgw"
 61  enable_telemetry    = true
 62  location            = module.avm-res-resources-resourcegroup.resource.location
 63  resource_group_name = module.avm-res-resources-resourcegroup.name
 64
 65  public_ips = {
 66    public_ip_1 = {
 67      name = "${var.name_prefix}-natgw-pip"
 68    }
 69  }
 70}
 71
 72module "avm-res-network-virtualnetwork" {
 73  source  = "Azure/avm-res-network-virtualnetwork/azurerm"
 74  version = "0.8.1"
 75
 76  resource_group_name = module.avm-res-resources-resourcegroup.name
 77  location            = module.avm-res-resources-resourcegroup.resource.location
 78  name                = "${var.name_prefix}-vnet"
 79
 80  address_space = [var.virtual_network_cidr]
 81
 82  subnets = {
 83    subnet0 = {
 84      name                            = "${var.name_prefix}-vm-subnet"
 85      default_outbound_access_enabled = false
 86      address_prefixes = [cidrsubnet(var.virtual_network_cidr, 1, 0)]
 87      nat_gateway = {
 88        id = module.avm-res-network-natgateway.resource_id
 89      }
 90      network_security_group = {
 91        id = module.avm-res-network-networksecuritygroup.resource_id
 92      }
 93    }
 94    bastion = {
 95      name                            = "AzureBastionSubnet"
 96      default_outbound_access_enabled = false
 97      address_prefixes = [cidrsubnet(var.virtual_network_cidr, 1, 1)]
 98    }
 99  }
100
101  diagnostic_settings = {
102    sendToLogAnalytics = {
103      name                           = "${var.name_prefix}-vnet-diagnostic"
104      workspace_resource_id          = module.avm-res-operationalinsights-workspace.resource_id
105      log_analytics_destination_type = "Dedicated"
106    }
107  }
108}
109
110module "avm-res-network-bastionhost" {
111  source  = "Azure/avm-res-network-bastionhost/azurerm"
112  version = "0.7.2"
113
114  name                = "${var.name_prefix}-bastion"
115  resource_group_name = module.avm-res-resources-resourcegroup.name
116  location            = module.avm-res-resources-resourcegroup.resource.location
117  ip_configuration = {
118    subnet_id = module.avm-res-network-virtualnetwork.subnets["bastion"].resource_id
119  }
120
121  diagnostic_settings = {
122    sendToLogAnalytics = {
123      name                           = "${var.name_prefix}-bastion-diagnostic"
124      workspace_resource_id          = module.avm-res-operationalinsights-workspace.resource_id
125      log_analytics_destination_type = "Dedicated"
126    }
127  }
128}
129
130module "avm-res-network-networksecuritygroup" {
131  source  = "Azure/avm-res-network-networksecuritygroup/azurerm"
132  version = "0.4.0"
133  resource_group_name = module.avm-res-resources-resourcegroup.name
134  name                = "${var.name_prefix}-vm-subnet-nsg"
135  location            = module.avm-res-resources-resourcegroup.resource.location
136
137  security_rules = {
138    "rule01" = {
139      name                       = "${var.name_prefix}-ssh"
140      access                     = "Allow"
141      destination_address_prefix = "*"
142      destination_port_ranges    = ["22"]
143      direction                  = "Inbound"
144      priority                   = 200
145      protocol                   = "Tcp"
146      source_address_prefix      = "*"
147      source_port_range          = "*"
148    }
149  }
150}
151
152module "avm-res-compute-virtualmachine" {
153  source  = "Azure/avm-res-compute-virtualmachine/azurerm"
154  version = "0.19.1"
155
156  enable_telemetry    = true
157  location            = module.avm-res-resources-resourcegroup.resource.location
158  resource_group_name = module.avm-res-resources-resourcegroup.name
159  name                = "${var.name_prefix}-vm"
160  os_type             = "Linux"
161  sku_size            = "Standard_D2s_v5"
162  zone                = 1
163
164  source_image_reference = {
165    publisher = "Canonical"
166    offer     = "0001-com-ubuntu-server-focal"
167    sku       = "20_04-lts-gen2"
168    version   = "latest"
169  }
170
171  network_interfaces = {
172    network_interface_1 = {
173      name = "${var.name_prefix}-nic-${random_string.name_suffix.result}"
174      ip_configurations = {
175        ip_configuration_1 = {
176          name                          = "${var.name_prefix}-ipconfig-${random_string.name_suffix.result}"
177          private_ip_subnet_resource_id = module.avm-res-network-virtualnetwork.subnets["subnet0"].resource_id
178        }
179      }
180    }
181  }
182
183  diagnostic_settings = {
184    sendToLogAnalytics = {
185      name                           = "${var.name_prefix}-vm-diagnostic"
186      workspace_resource_id          = module.avm-res-operationalinsights-workspace.resource_id
187      log_analytics_destination_type = "Dedicated"
188    }
189  }
190}

AVM modules provide several key advantages over writing raw Terraform templates:

  1. Simplified Resource Configuration: AVM modules handle much of the complex configuration work behind the scenes
  2. Built-in Recommended Practices: The modules implement many of Microsoft’s recommended practices by default
  3. Consistent Outputs: Each module exposes a consistent set of outputs that can be easily referenced
  4. Reduced Boilerplate Code: What would normally require hundreds of lines of Terraform code can be accomplished in a fraction of the space

As you continue your journey with Azure and AVM, remember that this approach can be applied to more complex architectures as well. The modular nature of AVM allows you to mix and match components to build solutions that meet your specific needs while adhering to Microsoft’s Well-Architected Framework.

By using AVM modules as building blocks, you can focus more on your solution architecture and less on the intricacies of individual resource configurations, ultimately leading to faster development cycles and more reliable deployments.

Additional exercises

For additional learning, it can be helpful to experiment with modifying this solution. Here are some ideas you can try if you have time and would like to experiment further.

  1. Use the managed_identities interface to add a system assigned managed identity to the virtual machine and give it Key Vault Administrator rights on the Key Vault.
  2. Use the tags interface to assign tags directly to one or more resources.
  3. Add an Azure Monitoring Agent extension to the virtual machine resource.
  4. Add additional inputs like VM sku to your module to make it more customizable. Be sure to update the code and tfvars files to match.

Clean up your environment

Once you have completed this set of exercises, it is a good idea to clean up your resources to avoid incurring costs for them. This can be done typing terraform destroy -var-file=development.tfvars and entering yes when prompted.

Solution Development

Considerations and steps of Solution Development

  • Decide on the IaC language (Bicep or Terraform)
  • Decide on the module sourcing method (public registry, private registry, inner-sourcing)
  • Decide on the orchestration method (template or pipeline)
  • Identify the resources needed for the solution (are they all available in AVM?)
  • Implement, validate, deploy, test the solution

Questions to cover on this page

  • Pick a realistically complex solution and demonstrate how to build it using AVM modules
  • Best practices for coding (link to official language specific guidance AND AVM specs where/if applicable)
  • Best practices for input and output parameters

Next steps

To be covered in separate, future articles.

To make this solution enterprise-ready, you need to consider the following:

  • Deploy with DevOps tools and practices (e.g., CI/CD in Azure DevOps, GitHub Actions, etc.)
  • Deploy into Azure Landing Zones (ALZ)
  • Make sure the solution follows the recommendations of the Well-Architected Framework (WAF) and it’s compliant with and integrates into your organization’s policies and standards, e.g.:
    • Security & Identity (e.g., RBAC, Entra ID, service principals, secrets management, MFA, etc.)
    • Networking (e.g., Azure Firewall, NSGs, etc.)
    • Monitoring (e.g., Azure Monitor, Log Analytics, etc.)
    • Cost management (e.g., Azure Cost Management, budgets, etc.)
    • Governance (e.g., Azure Policy, etc.)

Other recommendations

  • Don’t use latest, but a specific version of the module
  • Don’t expose secrets in output parameters/command line/logs/etc.
  • Don’t use hard-coded values, but use parameters and variables

Quickstart Guide

This QuickStart guide offers step-by-step instructions for integrating Azure Verified Modules (AVM) into your solutions. It includes the initial setup, essential tools, and configurations required to deploy and manage your Azure resources efficiently using AVM.

The AVM Key Vault resource module, used as an example in this chapter, simplifies the deployment and management of Azure Key Vaults, ensuring secure storage and access to your secrets, keys, and certificates.

Leveraging Azure Verified Modules

Using AVM ensures that your infrastructure-as-code deployments follow Microsoft’s best practices and guidelines, providing a consistent and reliable foundation for your cloud solutions. AVM helps accelerate your development process, reduce the risk of misconfigurations, and enhance the security and compliance of your applications.

Using default values

The default values provided by AVM are generally safe, as they follow best practices and ensure a secure and reliable setup. However, it is important to review these values to ensure they meet your specific requirements and compliance needs. Customizing the default values may be necessary to align with your organization’s policies and the specific needs of your solution.

Exploring examples and module features

You can find examples and detailed documentation for each AVM module in their respective code repository’s README.MD file, which details features, input parameters, and outputs. The module’s documentation also provides comprehensive usage examples, covering various scenarios and configurations. Additionally, you can explore the module’s source code repository. This information will help you understand the full capabilities of the module and how to effectively integrate it into your solutions.

Subsections of Quickstart

Bicep Quickstart Guide

Introduction

This guide explains how to use an Azure Verified Modules (AVM) in your Bicep workflow. By leveraging AVM modules, you can rapidly deploy and manage Azure infrastructure without having to write extensive code from scratch.

In this guide, you will deploy a Key Vault resource and a Personal Access Token as a secret.

This article is intended for a typical ‘infra-dev’ user (cloud infrastructure professional) who has a basic understanding of Azure and Bicep but is new to Azure Verified Modules and wants to learn how to deploy a module in the easiest way using AVM.

For additional Bicep learning resources use the Bicep documentation on the Microsoft Learn platform, or leverage the Fundamentals of Bicep learning path.

Prerequisites

You will need the following tools and components to complete this guide:

Make sure you have these tools set up before proceeding.

Module Discovery

Find your module

In this scenario, you need to deploy a Key Vault resource and some of its child resources, such as a secret. Let’s find the AVM module that will help us achieve this.

There are two primary ways for locating published Bicep Azure Verified Modules:

  • Option 1 (preferred): Using IntelliSense in the Bicep extension of Visual Studio Code, and
  • Option 2: browsing the AVM Bicep module index.

Option 1: Use the Bicep Extension in VS Code

  1. In VS Code, create a new file called main.bicep.
  2. Start typing module, then give your module a symbolic name, such as myModule.
  3. Use IntelliSense to select br/public.
  4. The list of all AVM modules published in the Bicep Public Registry will show up. Use this to explore the published modules.
    Note

    The Bicep VSCode extension is reading metadata through this JSON file. All modules are added to this file, as part of the publication process. This lists all the modules marked as Published or Orphaned on the AVM Bicep module index pages.

  5. Select the module you want to use and the version you want to deploy. Note how you can type full or partial module names to filter the list.
  6. Right click on the module’s path and select Go to definition or hit F12 to see the module’s source code. You can toggle between the Bicep and the JSON view.
  7. Hover over the module’s symbolic name to view its documentation URL. By clicking on it, you will be directed to the module’s GitHub folder in the bicep-registry-modules (BRM) repository. There, you can access the source code and documentation, as illustrated below.

Option 2: Use the AVM Bicep Module Index

Searching the Azure Verified Modules indexes is the most complete way to discover published as well as planned (proposed) modules. As shown in the video above, use the following steps to locate a specific module on the AVM website:

  1. Open the AVM website in your favorite web browser: https://aka.ms/avm.
  2. Expand the Module Indexes menu item and select the Bicep sub-menu item.
  3. Select the menu item for the module type you are searching for: Resource, Pattern, or Utility.
    Note

    Since the Key Vault module used as an example in this guide is published as an AVM resource module, it can be found under the resource modules section in the AVM Bicep module index.

  4. A detailed description of module classification types can be found under the related section here.
  5. Select the Published modules link from the table of contents at the top of the page.
  6. Use the in-page search feature of your browser. In most Windows browsers you can access it using the CTRL + F keyboard shortcut.
  7. Enter a search term to find the module you are looking for - e.g., Key Vault.
  8. Move through the search results until you locate the desired module. If you are unable to find a published module, return to the table of contents and expand the All modules link to search both published and proposed modules - i.e., modules that are planned, likely in development but not published yet.
  9. After finding the desired module, click on the module’s name. This link will lead you to the module’s folder in the bicep-registry-modules (BRM) repository, where the module’s source code and documentation can be found, including usage examples.

Module details and examples

In the module’s documentation, you can find detailed information about the module’s functionality, components, input parameters, outputs and more. The documentation also provides comprehensive usage examples, covering various scenarios and configurations.

Explore the Key Vault module’s documentation for usage examples and to understand its functionality, input parameters, and outputs.

  1. Note the mandatory and optional parameters in the Parameters section.

  2. Review the Usage examples section. AVM modules include multiple tests that can be found under the tests folder. These tests are also used as the basis of the usage examples ensuring they are always up-to-date and deployable.

In this example, you will deploy a secret in a new Key Vault instance with minimal input. AVM provides default parameter values with security and reliability being core principles. These settings apply the recommendations of the Well Architected Framework where possible and appropriate.

Note how Example 2 does most of what you need to achieve.

Create your new solution using AVM

In this section, you will develop a Bicep template that references the AVM Key Vault module and its child resources and features. These include secret and role based access control configurations that grant permissions to a user.

  1. Start VSCode (make sure the Bicep extension is installed) and open a folder in which you want to work.
  2. Create a main.bicep and a dev.bicepparam file, which will hold parameters for your Key Vault deployment.
  3. Copy the content below into your main.bicep file. We have included comments to distinguish between the two different occurrences of the names attribute.
module myKeyVault 'br/public:avm/res/key-vault/vault:0.11.0' = {
  name: // the name of the module's deployment
  params: {
    name: '<keyVaultName>' // the name of the Key Vault instance - length and character limits apply
  }
}
Note

For Azure Key Vaults, the name must be globally unique. When you deploy the Key Vault, ensure you select a name that is alphanumeric, twenty-four characters or less, and unique enough to ensure no one else has used the name for their Key Vault. If the name has been previously taken, you will get an error.

After setting the values for the required properties, the module can be deployed. This minimal configuration automatically applies the security and reliability recommendations of the Well Architected Framework where possible and appropriate. These settings can be overridden if needed.

Bicep-specific configuration

It is recommended to create a bicepconfig.json file, and enable use-recent-module-versions, which warns you to use the latest available version of the AVM module.

// This is a Bicep configuration file. It can be used to control how Bicep operates and to customize
// validation settings for the Bicep linter. The linter relies on these settings when evaluating your
// Bicep files for best practices. For further information, please refer to the official documentation at:
// https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-config
{
  "analyzers": {
    "core": {
      "rules": {
        "use-recent-module-versions": {
          "level": "warning",
          "message": "The module version is outdated. Please consider updating to the latest version."
        }
      }
    }
  }
}

Define the Key Vault instance

In this scenario - and every other real-world setup - there is more that you need to configure. You can open the module’s documentation by hovering over its symbolic name to see all of the module’s capabilities - including supported parameters.

Note

The Bicep extension facilitates code-completion, enabling you to easily locate and utilize the Azure Verified Module. This feature also provides the necessary properties for a module, allowing you to begin typing and leverage IntelliSense for completion.

  1. Add parameters and values to the main.bicep file to customize your configuration. These parameters are used for passing in the Key Vault name and enabling purge protection. You might not want to enable the latter in a non-production environment, as it makes it harder to delete and recreate resources.

The main.bicep file will now look like this:

// the scope, the deployment deploys resources to
targetScope = 'resourceGroup'

// parameters and default values
param keyVaultName string

@description('Disable for development deployments.')
param enablePurgeProtection bool = true

// the resources to deploy
module myKeyVault 'br/public:avm/res/key-vault/vault:0.11.0' = {
  name: 'key-vault-deployment'
  params: {
    name: keyVaultName
    enablePurgeProtection: enablePurgeProtection
    // more properties are not needed, as AVM provides default values
  }
}

Note that the Key Vault instance will be deployed within a resource group scope in our example.

  1. Create a dev.bicepparam file (this is optional) and set parameter values for your environment. You can now pass these values by referencing this file at the time of deployment (using PowerShell or Azure CLI).
using 'main.bicep'

// environment specific values
param keyVaultName = '<keyVaultName>'
param enablePurgeProtection = false

Create a secret and set permissions

Add a secret to the Key Vault instance and grant permissions to a user to work with the secret. Sample role assignments can be found in Example 3: Using large parameter set. See Parameter: roleAssignments for a list of pre-defined roles that you can reference by name instead of a GUID. This is a key benefit of using AVM, as the code is easy to read and increases the maintainability.

You can also leverage User-defined data types and simplify the parameterization of the modules instead of guessing or looking up parameters. Therefore, first import UDTs from the Key Vault and common types module and leverage the UDTs in your Bicep and parameter files.

For a role assignment, the principal ID is needed, that will be granted a role (specified by its name) on the resource. Your own ID can be found out with az ad signed-in-user show --query id.

// the scope, the deployment deploys resources to
targetScope = 'resourceGroup'

// parameters and default values
param keyVaultName string
// the PAT token is a secret and should not be stored in the Bicep(parameter) file.
// It can be passed via the commandline, if you don't use a parameter file.
@secure()
param patToken string = newGuid()

@description('Enabled by default. Disable for development deployments')
param enablePurgeProtection bool = true

import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.4.0'
// the role assignments are optional in the Key Vault module
param roleAssignments roleAssignmentType[]?

// the resources to deploy
module myKeyVault 'br/public:avm/res/key-vault/vault:0.11.0' = {
  name: 'key-vault-deployment'
  params: {
    name: keyVaultName
    enablePurgeProtection: enablePurgeProtection
    secrets: [
      {
        name: 'PAT'
        value: patToken
      }
    ]
    roleAssignments: roleAssignments
  }
}

The secrets parameter references a UDT (User-defined data type) that is part of the Key Vault module and enables code completion for easy usage. There is no need to look up what attributes the secret object might have. Start typing and tab-complete what you need from the content offered by the Bicep extension’s integration with AVM.

The bicep parameter file now looks like this:

// reference to the Bicep file to set the context
using 'main.bicep'

// environment specific values
param keyVaultName = '<keyVaultName>'
param enablePurgeProtection = false
// for security reasons, the secret value must not be stored in this file.
// You can change it later in the deployed Key Vault instance, where you also renew it after expiration.

param roleAssignments = [
  {
    principalId: '<principalId>'
    // using the name of the role instead of looking up the GUID (which can also be used)
    roleDefinitionIdOrName: 'Key Vault Secrets Officer'
  }
]
Note

The display names for roleDefinitionIdOrName can be acquired the following two ways:

  • From the parameters section of the module’s documentation.
  • From the builtInRoleNames variable in the module’s source code. To get there, hit F12 while the cursor is on the part of the module path starting with br/public:.

Boost your development with VS Code IntelliSense

Leverage the IntelliSense feature in VS Code to speed up your development process. IntelliSense provides code completion, possible parameter values and structure. It helps you write code more efficiently by providing context-aware suggestions as you type.

Here is how quickly you can deliver the solution detailed in this section:

Deploy your solution

Now that your template and parameter file is ready, you can deploy your solution to Azure. Use PowerShell or the Azure CLI to deploy your solution.

Deploy with
# Log in to Azure
Connect-AzAccount

# Select your subscription
Set-AzContext -SubscriptionId '<subscriptionId>'

# Deploy a resource group
New-AzResourceGroup -Name 'avm-quickstart-rg' -Location 'germanywestcentral'

# Invoke your deployment
New-AzResourceGroupDeployment -DeploymentName 'avm-quickstart-deployment' -ResourceGroupName 'avm-quickstart-rg' -TemplateParameterFile 'dev.bicepparam' -TemplateFile 'main.bicep'
# Log in to Azure
az login

# Select your subscription
az account set --subscription '<subscriptionId>'

# Deploy a resource group
az group create --name 'avm-quickstart-rg' --location 'germanywestcentral'

# Invoke your deployment
az deployment group create --name 'avm-quickstart' --resource-group 'avm-quickstart-rg' --template-file 'main.bicep' --parameters 'dev.bicepparam'

Use the Azure portal, Azure PowerShell, or the Azure CLI to verify that the Key Vault instance and secret have been successfully created with the correct configuration.

Clean up your environment

When you are ready, you can remove the infrastructure deployed in this example. The following commands will remove all resources created by your deployment:

Clean up with
# Delete the resource group
Remove-AzResourceGroup -Name "avm-quickstart-rg" -Force

# Purge the Key Vault
Remove-AzKeyVault -VaultName "<keyVaultName>" -Location "germanywestcentral" -InRemovedState -Force
# Delete the resource group
az group delete --name 'avm-quickstart-rg' --yes --no-wait

# Purge the Key Vault
az keyvault purge --name '<keyVaultName>' --no-wait

Congratulations, you have successfully leveraged an AVM Bicep module to deploy resources in Azure!

Tip

We welcome your contributions and feedback to help us improve the AVM modules and the overall experience for the community!

Next Steps

For developing a more advanced solution, please see the lab titled “Introduction to using Azure Verified Modules for Bicep”.

Terraform Quickstart Guide

Introduction

This guide explains how to use an Azure Verified Modules (AVM) in your Terraform workflow. With AVM modules, you can quickly deploy and manage Azure infrastructure without writing extensive code from scratch.

In this guide, you will deploy a Key Vault resource and generate and store a key.

This article is intended for a typical ‘infra-dev’ user (cloud infrastructure professional) who is new to Azure Verified Modules and wants to learn how to deploy a module in the easiest way using AVM. The user has a basic understanding of Azure and Terraform.

For additional Terraform resources, try a tutorial on the HashiCorp website or study the detailed documentation.

Prerequisites

You will need the following tools and components to complete this guide:

Before you begin, ensure you have these tools installed in your development environment.

Module Discovery

Find your module

In this scenario, you need to deploy a Key Vault resource and some of its child resources, such as a key. Let’s find the AVM module that will help us achieve this.

There are two primary ways for locating published Terraform Azure Verified Modules:

Use the Terraform Registry

The easiest way to find published AVM Terraform modules is by searching the Terraform Registry. Follow these steps to locate a specific module, as shown in the video above.

  • Use your web browser to go to the HashiCorp Terraform Registry
  • In the search bar at the top of the screen type avm. Optionally, append additional search terms to narrow the search results. (e.g., avm key vault for AVM modules with Key Vault in the name.)
  • Select see all to display the full list of published modules matching your search criteria.
  • Find the module you wish to use and select it from the search results.
Note

It is possible to discover other unofficial modules with avm in the name using this search method. Look for the Partner tag in the module title to determine if the module is part of the official set.

Use the AVM Terraform Module Index

Searching the Azure Verified Modules indexes is the most complete way to discover published as well as planned modules - shown as proposed. As presented in the video above, use the following steps to locate a specific module on the AVM website:

  • Use your web browser to open the AVM website at https://aka.ms/avm.
  • Expand the Module Indexes menu item and select the Terraform sub-menu item.
  • Select the menu item for the module type you are searching for: Resource, Pattern, or Utility.
    Note

    Since the Key Vault module used as an example in this guide is published as an AVM resource module, it can be found under the resource modules section in the AVM Terraform module index.

  • A detailed description of each module classification type can be found under the related section here.
  • Select the Published modules link from the table of contents at the top of the page.
  • Use the in-page search feature of your browser (in most Windows browsers you can access it using the CTRL + F keyboard shortcut).
  • Enter a search term to find the module you are looking for - e.g., Key Vault.
  • Move through the search results until you locate the desired module. If you are unable to find a published module, return to the table of contents and expand the All modules link to search both published and proposed modules - i.e., modules that are planned, likely in development but not published yet.
  • After finding the desired module, click on the module’s name. This link will lead you to the official HashiCorp Terraform Registry page for the module where you can find the module’s documentation and examples.

Module details and examples

Once you have identified the AVM module in the Terraform Registry you can find detailed information about the module’s functionality, components, input parameters, outputs and more. The documentation also provides comprehensive usage examples, covering various scenarios and configurations.

Explore the Key Vault module’s documentation and usage examples to understand its functionality, input variables, and outputs.

  • Note the Examples drop-down list and explore each example
  • Review the Readme tab to see module provider minimums, a list of resources and data sources used by the module, a nicely formatted version of the inputs and outputs, and a reference to any submodules that may be called.
  • Explore the Inputs tab and observe how each input has a detailed description and a type definition for you to use when adding input values to your module configuration.
  • Explore the Outputs tab and review each of the outputs that are exported by the AVM module for use by other modules in your deployment.
  • Finally, review the Resources tab to get a better understanding of the resources defined in the module.

In this example, you will deploy a secret in a new Key Vault instance without needing to provide other parameters. The AVM Key Vault resource module provides these capabilities and does so with security and reliability being core principles. The default settings of the module also apply the recommendations of the Well Architected Framework where possible and appropriate.

Note how the create-key example seems to do what you need to achieve.

Create your new solution using AVM

Now that you have found the module details, you can use the content from the Terraform Registry to speed up your development in the following ways:

  1. Option 1: Create a solution using AVM module examples: duplicate a module example and edit it for your needs. This is useful if you are starting without any existing infrastructure and need to create supporting resources like resource groups as part of your deployment.
  2. Option 2: Create a solution by changing the AVM module input values: add the AVM module to an existing solution that already includes other resources. This method requires some knowledge of the resource(s) being deployed so that you can make choices about optional features configured in your solution’s version of the module.

Each deployment method includes a section below so that you can choose the method which best fits your needs.

Note

For Azure Key Vaults, the name must be globally unique. When you deploy the Key Vault, ensure you select a name that is alphanumeric, twenty-four characters or less, and unique enough to ensure no one else has used the name for their Key Vault. If the name has been used previously, you will get an error.

Option 1: Create a solution using AVM module examples

Leverage the following steps as a template for how to leverage examples for bootstrapping your new solution code. The Key Vault resource module is used here as an example, but in practice you may choose any module that applies to your scenario.

  • Locate and select the Examples drop down menu in the middle of the Key Vault module page.
  • From the drop-down list select an example whose name most closely aligns with your scenario - e.g., create-key.
  • When the example page loads, read the example description to determine if this is the desired example. If it is not, return to the module main page, and select a different example until you are satisfied that the example covers the scenario you are trying to deploy. If you are unable to find a suitable example, leverage the last two steps in the option 2 instructions to modify the inputs of the selected example to match your requirements.
  • Scroll to the code block for the example and select the Copy button on the top right of the block to copy the content to the clipboard.
➕ Click here to copy the sample code from the video.
provider "azurerm" {
  features {}
}

terraform {
  required_version = "~> 1.9"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.71"
    }
    http = {
      source  = "hashicorp/http"
      version = "~> 3.4"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.5"
    }
  }
}

module "regions" {
  source  = "Azure/avm-utl-regions/azurerm"
  version = "0.1.0"
}

# This allows us to randomize the region for the resource group.
resource "random_integer" "region_index" {
  max = length(module.regions.regions) - 1
  min = 0
}

# This ensures you have unique CAF compliant names for our resources.
module "naming" {
  source  = "Azure/naming/azurerm"
  version = "0.3.0"
}

resource "azurerm_resource_group" "this" {
  location = module.regions.regions[random_integer.region_index.result].name
  name     = module.naming.resource_group.name_unique
}

# Get current IP address for use in KV firewall rules
data "http" "ip" {
  url = "https://api.ipify.org/"
  retry {
    attempts     = 5
    max_delay_ms = 1000
    min_delay_ms = 500
  }
}

data "azurerm_client_config" "current" {}

module "key_vault" {
  source                        = "Azure/avm-res-keyvault-vault/azurerm"
  name                          = module.naming.key_vault.name_unique
  location                      = azurerm_resource_group.this.location
  enable_telemetry              = var.enable_telemetry
  resource_group_name           = azurerm_resource_group.this.name
  tenant_id                     = data.azurerm_client_config.current.tenant_id
  public_network_access_enabled = true
  keys = {
    cmk_for_storage_account = {
      key_opts = [
        "decrypt",
        "encrypt",
        "sign",
        "unwrapKey",
        "verify",
        "wrapKey"
      ]
      key_type: "RSA"
      name     = "cmk-for-storage-account"
      key_size = 2048
    }
  }
  role_assignments = {
    deployment_user_kv_admin = {
      role_definition_id_or_name = "Key Vault Administrator"
      principal_id               = data.azurerm_client_config.current.object_id
    }
  }
  wait_for_rbac_before_key_operations = {
    create = "60s"
  }
  network_acls = {
    bypass   = "AzureServices"
    ip_rules = ["${data.http.ip.response_body}/32"]
  }
}
  • In your IDE - Visual Studio Code in our example - create the main.tf file for your new solution.

  • Paste the content from the clipboard into main.tf.

  • AVM examples frequently use naming and/or region selection AVM utility modules to generate deployment region and/or naming values as well as any default values for required fields. If you want to use a specific region name or other custom resource values, remove the existing region and naming module calls and replace example input values with the new desired custom input values.

  • Once supporting resources such as resource groups have been modified, locate the module call for the AVM module - i.e., module "keyvault".

  • AVM module examples use dot notation for a relative reference that is useful during module testing. However, you will need to replace the relative reference with a source reference that points to the Terraform Registry source location. In most cases, this source reference has been left as a comment in the module example to simplify replacing the existing source dot reference. Perform the following two actions to update the source:

    • Delete the existing source definition that uses a dot reference - i.e., source = "../../".
    • Uncomment the Terraform Registry source reference by deleting the # sign at the start of the commented source line - i.e., source = "Azure/avm-res-keyvault-vault/azurerm".
    Note

    If the module example does not include a commented Terraform Registry source reference, you will need to copy it from the module’s main documentation page. Use the following steps to do so:

    • Use the breadcrumbs to leave the example documentation and return to the module’s primary Terraform Registry documentation page.
    • Locate the Provision Instructions box on the right side of the module’s Terraform Registry page in your web browser.
    • Select the second line that starts with source = from the code block - e.g., source = "Azure/avm-res-keyvault-vault/azurerm". Copy it onto the clipboard.
    • Return to your code solution and Paste the clipboard’s content where you previously deleted the source dot reference - e.g., source = "../../".
  • AVM module examples use a variable to enable or disable the telemetry collection. Update the enable_telemetry input value to true or false. - e.g. enable_telemetry = true

  • Save your main.tf file changes and then proceed to the guide section for running your solution code.

Option 2: Create a solution by changing the AVM module input values

Click here to copy the sample code from the video.
module "avm-res-keyvault-vault" {
  source                        = "Azure/avm-res-keyvault-vault/azurerm"
  version                       = "0.9.1"
  name                          = "<custom_name_here>"
  resource_group_name           = azurerm_resource_group.this.name
  location                      = azurerm_resource_group.this.location
  tenant_id                     = data.azurerm_client_config.this.tenant_id

  keys = {
    cmk_for_storage_account = {
      key_opts = [
        "decrypt",
        "encrypt",
        "sign",
        "unwrapKey",
        "verify",
        "wrapKey"
      ]
      key_type: "RSA"
      name     = "cmk-for-storage-account"
      key_size = 2048
    }
  }
  role_assignments = {
    deployment_user_kv_admin = {
      role_definition_id_or_name = "Key Vault Administrator"
      principal_id               = data.azurerm_client_config.current.object_id
    }
  }
  wait_for_rbac_before_key_operations = {
    create = "60s"
  }
}

Use the following steps as a guide for the custom implementation of an AVM Module in your solution code. This instruction path assumes that you have an existing Terraform file that you want to add the AVM module to.

  • Locate the Provision Instructions box on the right side of the module’s Terraform Registry page in your web browser.
  • Select the module template code from the code block and Copy it onto the clipboard.
  • Switch to your IDE and Paste the contents of the clipboard into your solution’s .tf Terraform file - main.tf in our example.
  • Return to the module’s Terraform Registry page in the browser and select the Inputs tab.
  • Review each input and add the inputs with the desired target value to the solution’s code - i.e., name = "custom_name".
  • Once you are satisfied that you have included all required inputs and any optional inputs, Save your file and continue to the next section.

Deploy your solution

After completing your solution development, you can move to the deployment stage. Follow these steps for a basic Terraform workflow:

  • Open the command line and login to Azure using the Azure cli

    az login
  • If your account has access to multiple tenants, you may need to modify the command to az login --tenant <tenant id> where “<tenant id>” is the guid for the target tenant.

  • After logging in, select the target subscription from the list of subscriptions that you have access to.

  • Change the path to the directory where your completed terraform solution files reside.

    Note

    Many AVM modules depend on the AzureRM 4.0 Terraform provider which mandates that a subscription id is configured. If you receive an error indicating that subscription_id is a required provider property, you will need to set a subscription id value for the provider. For Unix based systems (Linux or MacOS) you can configure this by running export ARM_SUBSCRIPTION_ID=<your subscription guid> on the command line. On Microsoft Windows, you can perform the same operation by running set ARM_SUBSCRIPTION_ID="<your subscription guid>" from the Windows command prompt or by running $env:ARM_SUBSCRIPTION_ID="<your subscription guid>" from a powershell prompt. Replace the “<your subscription id>” notation in each command with your Azure subscription’s unique id value.

  • Initialize your Terraform project. This command downloads the necessary providers and modules to the working directory.

    terraform init
  • Before applying the configuration, it is good practice to validate it to ensure there are no syntax errors.

    terraform validate
  • Create a deployment plan. This step shows what actions Terraform will take to reach the desired state defined in your configuration.

    terraform plan
  • Review the plan to ensure that only the desired actions are in the plan output.

  • Apply the configuration and create the resources defined in your configuration file. This command will prompt you to confirm the deployment prior to making changes. Type yes to create your solution’s infrastructure.

    terraform apply
    Info

    If you are confident in your changes, you can add the -auto-approve switch to bypass manual approval: terraform apply -auto-approve

  • Once the deployment completes, validate that the infrastructure is configured as desired.

    Info

    A local terraform.tfstate file and a state backup file have been created during the deployment. The use of local state is acceptable for small temporary configurations, but production or long-lived installations should use a remote state configuration where possible. Configuring remote state is out of scope for this guide, but you can find details on using an Azure storage account for this purpose in the Microsoft Learn documentation.

Clean up your environment

When you are ready, you can remove the infrastructure deployed in this example. Use the following command to delete all resources created by your deployment:

terraform destroy
Note

Most Key Vault deployment examples activate soft-delete functionality as a default. The terraform destroy command will remove the Key Vault resource but does not purge a soft-deleted vault. You may encounter errors if you attempt to re-deploy a Key Vault with the same name during the soft-delete retention window. If you wish to purge the soft-delete for this example you can run az keyvault purge -n <keyVaultName> -l <regionName> using the Azure CLI, or Remove-AzKeyVault -VaultName "<keyVaultName>" -Location "<regionName>" -InRemovedState using Azure PowerShell.

Congratulations, you have successfully leveraged Terraform and AVM to deploy resources in Azure!

Tip

We welcome your contributions and feedback to help us improve the AVM modules and the overall experience for the community!

Next Steps

For developing a more advanced solution, please see the lab titled “Introduction to using Azure Verified Modules for Terraform”.

Subsections of Specifications & Definitions

Module Specifications

This section documents all the specifications for Azure Verified Modules (AVM) and their respective IaC languages.

Specifications by IaC Language

CategoryBicepTerraform
ResourcePatternUtilityResourcePatternUtility
Contribution/Support988988
Telemetry434222
Naming/Composition2518917127
CodeStyle222292929
Inputs/Outputs141110865
Testing14131310109
Documentation555444
Release/Publishing555444
Summary786556837568

What changed recently?

No specifications were changed in the last 30 days.

How to navigate the specifications?

The “Module Specifications” section uses tags to dynamically render content based on the selected attributes, such as the IaC language, module classification, category, severity and more. The tags are defined in header of each specification page.

To make it easier for module owners and contributors to navigate the documentation, the specifications are grouped to distinct pages by the IaC language (Bicep | Terraform) and module classification ( resource | pattern | utility). The specifications on each page are further ordered by the category (e.g., Composition, CodeStyle, Testing, etc.), severity of the requirements (MUST | SHOULD | MAY) and at what stage of the module’s lifecycle the specification is typically applicable (Initial | BAU | EOL).

To find what you need, simply decide which IaC language you’d like develop in and what classification your module falls under, then navigate to the respective page to find the specifications that are relevant to you.

Info

All specifications have a 4-9 character long unique ID - a combination of letters and numbers. These letters only carry legacy meaning only leveraged by the AVM core team and are no longer used to group the specifications in any visible way. The ID is used to reference the specification in the code, documentation, and discussions.

Specification Tags

The following tags are used to qualify the specifications:

KeyAllowed ValuesMultiple/Single
LanguageBicep, TerraformMultiple
ClassResource, Pattern, UtilityMultiple
TypeFunctional, NonFunctionalSingle
CategoryTesting, Telemetry, Contribution/Support, Documentation, CodeStyle, Naming/Composition, Inputs/Outputs, Release/PublishingSingle
SeverityMUST, SHOULD, MAYSingle
PersonaOwner, ContributorMultiple
LifecycleInitial, BAU, EOLSingle
ValidationBicep: BCP/Manual, BCP/CI/Informational, BCP/CI/Enforced
Terraform: TF/Manual, TF/CI/Informational, TF/CI/Enforced
Single per language

Each tag is a concatenation of exactly one of the keys and one of the values, e.g., Language-Bicep, Class-Resource, Type-Functional, etc. When it’s marked as Multiple, it means that the tag can have multiple values, e.g., Language-Bicep, Language-Terraform, or Persona-Owner, Persona-Contributor, etc. When it’s marked as Single, it means that the tag can have only one value, e.g., Type-Functional, Lifecycle-Initial, etc.

➕ Click here to see the definition of the Severity, Persona, Lifecycle and Validation tags...

Severity

What’s the severity or importance of this specification? See “How to read the specifications?” section for more details.

Persona

Who is this specification for? The Owner is the module owner, while the Contributor is anyone who contributes to the module.

Lifecycle

When is this specification mostly relevant?

  • The Initial stage is when the module is being developed first - e.g., naming related specs are labeled with Lifecycle-Initial as the naming of the module only happens once: at the beginning of their life.
  • The BAU (business as usual) stage is at any time during the module’s typical lifecycle - e.g., specs that describe coding standards are relevant throughout the module’s life, for any time a new module version is released.
  • The EOL (end of life) stage is when the module is being decommissioned - e.g., specs describing how a module should be retired are labeled with Lifecycle-EOL.

Validation

How is this specification checked/validated/enforced?

  • Manual means that the specification is manually enforced at the time of the module review (at the time of the first or any subsequent module version release).
  • CI/Informational means that the module is checked against the specification by a CI pipeline, but the failure is only informational and doesn’t block the module release.
  • CI/Enforced means that the specification is automatically enforced by a CI pipeline, and the failure blocks the module release.

Note: the BCP/ or TF/ prefix is required as shared (language-agnostic) specifications may have different level of validation/enforcement per each language - e.g., it is possible that a specification is enforced by a CI pipeline for Bicep modules, while it is manually enforced for Terraform modules.

Why are there language specific specifications?

While every effort is being made to standardize requirements and implementation details across all languages (and most specifications in fact, are applicable to all), 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.

How to read the specifications?

Important

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.

As you’re developing/maintaining a module as a module owner or contributor, you need to ensure that your module adheres to the specifications outlined in this section. The specifications are designed to ensure that all AVM modules are consistent, secure, and compliant with best practices.

There are 3 levels of specifications:

  • MUST: These are mandatory requirements that MUST be followed.
  • SHOULD: These are recommended requirements that SHOULD be followed, unless there are good reasons for not to.
  • MAY: These are optional requirements that MAY be followed at the module owner’s/contributor’s discretion.

Subsections of Module Specifications

Bicep Specifications

Specifications by Category and Module Classification

CategoryResourcePatternUtility
Contribution/Support988
Telemetry434
Naming/Composition25189
CodeStyle222
Inputs/Outputs141110
Testing141313
Documentation555
Release/Publishing555
Summary786556

How to propose changes to the specifications?

Important

Any updates to existing or new specifications for Bicep must be submitted as a draft for review by the AVM core team(@Azure/avm-core-team).

What changed recently?

No specifications were changed in the last 30 days.

Subsections of Bicep

Bicep Interfaces

This chapter details the interfaces/schemas for the AVM Resource Modules features/extension resources as referenced in RMFR4 and RMFR5.

Diagnostic Settings

Important

Allowed values for logs and metric categories or category groups MUST NOT be specified to keep the module implementation evergreen for any new categories or category groups added by RPs, without module owners having to update a list of allowed values and cut a new release of their module.

Diagnostic Settings
  
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. The diagnostic settings of the service. If neither metrics nor logs are specified, all metrics & logs are configured by default. If only one of them is specified, the other one will not be configured.')
  param diagnosticSettings diagnosticSettingFullType[]?
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType<_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [for (diagnosticSetting, index) in (diagnosticSettings ?? []): {
    name: diagnosticSetting.?name ?? '${name}-diagnosticSettings'
    properties: {
      storageAccountId: diagnosticSetting.?storageAccountResourceId
      workspaceId: diagnosticSetting.?workspaceResourceId
      eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId
      eventHubName: diagnosticSetting.?eventHubName
      metrics: [
        for group in (diagnosticSetting.?metricCategories ?? (empty(diagnosticSetting.?logCategoriesAndGroups)
          ? [{ category: 'AllMetrics' }]
          : [])): {
          category: group.category
          enabled: group.?enabled ?? true
          timeGrain: null
        }
      ]
      logs: [
        for group in (diagnosticSetting.?logCategoriesAndGroups ?? (empty(diagnosticSetting.?metricCategories)
          ? [{ categoryGroup: 'allLogs' }]
          : [])): {
          categoryGroup: group.?categoryGroup
          category: group.?category
          enabled: group.?enabled ?? true
        }
      ]
      marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId
      logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType
    }
    scope: >singularMainResourceType<
  }]
  
  diagnosticSettings: [
    {
      name: 'diagSetting1'
      logCategoriesAndGroups: [
        {
          category: 'AzurePolicyEvaluationDetails'
        }
        {
          category: 'AuditEvent'
        }
      ]
      metricCategories: [
        {
          category: 'AllMetrics'
        }
      ]
      logAnalyticsDestinationType: 'Dedicated'
      workspaceResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}'
      storageAccountResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{storageAccountName}'
      eventHubAuthorizationRuleResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.EventHub/namespaces/{namespaceName}/eventhubs/{eventHubName}/authorizationrules/{authorizationRuleName}'
      eventHubName: '{eventHubName}'
      marketplacePartnerResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{partnerResourceProvider}/{partnerResourceType}/{partnerResourceName}'
    }
  ]
  
  
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { diagnosticSettingMetricsOnlyType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. The diagnostic settings of the service.')
  param diagnosticSettings diagnosticSettingMetricsOnlyType[]?
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType<_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [for (diagnosticSetting, index) in (diagnosticSettings ?? []): {
    name: diagnosticSetting.?name ?? '${name}-diagnosticSettings'
    properties: {
      storageAccountId: diagnosticSetting.?storageAccountResourceId
      workspaceId: diagnosticSetting.?workspaceResourceId
      eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId
      eventHubName: diagnosticSetting.?eventHubName
      metrics: [for group in (diagnosticSetting.?metricCategories ?? [ { category: 'AllMetrics' } ]): {
        category: group.category
        enabled: group.?enabled ?? true
        timeGrain: null
      }]
      marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId
      logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType
    }
    scope: >singularMainResourceType<
  }]
  
  diagnosticSettings: [
    {
      name: 'diagSetting1'
      metricCategories: [
        {
          category: 'AllMetrics'
        }
      ]
      logAnalyticsDestinationType: 'Dedicated'
      workspaceResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}'
      storageAccountResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{storageAccountName}'
      eventHubAuthorizationRuleResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.EventHub/namespaces/{namespaceName}/eventhubs/{eventHubName}/authorizationrules/{authorizationRuleName}'
      eventHubName: '{eventHubName}'
      marketplacePartnerResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{partnerResourceProvider}/{partnerResourceType}/{partnerResourceName}'
    }
  ]
  
  
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { diagnosticSettingLogsOnlyType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. The diagnostic settings of the service.')
  param diagnosticSettings diagnosticSettingLogsOnlyType[]?
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType<_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [for (diagnosticSetting, index) in (diagnosticSettings ?? []): {
    name: diagnosticSetting.?name ?? '${name}-diagnosticSettings'
    properties: {
      storageAccountId: diagnosticSetting.?storageAccountResourceId
      workspaceId: diagnosticSetting.?workspaceResourceId
      eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId
      eventHubName: diagnosticSetting.?eventHubName
      logs: [for group in (diagnosticSetting.?logCategoriesAndGroups ?? [ { categoryGroup: 'allLogs' } ]): {
        categoryGroup: group.?categoryGroup
        category: group.?category
        enabled: group.?enabled ?? true
      }]
      marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId
      logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType
    }
    scope: >singularMainResourceType<
  }]
  
  diagnosticSettings: [
    {
      name: 'diagSetting1'
      logCategoriesAndGroups: [
        {
          category: 'AzurePolicyEvaluationDetails'
        }
        {
          category: 'AuditEvent'
        }
      ]
      logAnalyticsDestinationType: 'Dedicated'
      workspaceResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}'
      storageAccountResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{storageAccountName}'
      eventHubAuthorizationRuleResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.EventHub/namespaces/{namespaceName}/eventhubs/{eventHubName}/authorizationrules/{authorizationRuleName}'
      eventHubName: '{eventHubName}'
      marketplacePartnerResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{partnerResourceProvider}/{partnerResourceType}/{partnerResourceName}'
    }
  ]
  
Note

In the provided example for Diagnostic Settings, both logs and metrics are enabled for the associated resource. However, it is IMPORTANT to note that certain resources may not support both diagnostic setting types/categories. In such cases, the resource configuration MUST be modified accordingly to ensure proper functionality and compliance with system requirements.

Role Assignments

  
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. Array of role assignments to create.')
  param roleAssignments roleAssignmentType[]?
  
  // ============= //
  //   Variables   //
  // ============= //
  
  var builtInRoleNames = {
    // Add other relevant built-in roles here for your resource as per BCPNFR5
    Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
    Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')
    Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
    'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')
    'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')
  }
  
  var formattedRoleAssignments = [
    for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, {
      roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/')
            ? roleAssignment.roleDefinitionIdOrName
            : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName))
    })
  ]
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType<_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
    for (roleAssignment, index) in (formattedRoleAssignments ?? []): {
      name: roleAssignment.?name ?? guid(>singularMainResourceType<.id, roleAssignment.principalId, roleAssignment.roleDefinitionId)
      properties: {
        roleDefinitionId: roleAssignment.roleDefinitionId
        principalId: roleAssignment.principalId
        description: roleAssignment.?description
        principalType: roleAssignment.?principalType
        condition: roleAssignment.?condition
        conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set
        delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId
      }
      scope: >singularMainResourceType<
    }
  ]
  
  roleAssignments: [
    {
      roleDefinitionIdOrName: 'Owner'
      principalId: nestedDependencies.outputs.managedIdentityPrincipalId
      principalType: 'ServicePrincipal'
    }
    {
      roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
      principalId: nestedDependencies.outputs.managedIdentityPrincipalId
      principalType: 'ServicePrincipal'
    }
    {
      roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
      principalId: nestedDependencies.outputs.managedIdentityPrincipalId
      principalType: 'ServicePrincipal'
    }
    {
      name: guid('Custom role assignment name seed')
      roleDefinitionIdOrName: 'Storage Blob Data Reader'
      principalId: '00000000-0000-0000-0000-000000000000'
      principalType: 'Group'
      description: 'Group with read-only access'
      condition: '@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase 'foo_storage_container''
      conditionVersion: '2.0'
    }
  ]
  

Details on child, extension and cross-referenced resources:

  • Modules MUST support Role Assignments on child, extension and cross-referenced resources as well as the primary resource via parameters/variables

Resource Locks

  // ============== //
  //   Parameters   //
  // ============== //
  
  import { lockType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. The lock settings of the service.')
  param lock lockType?
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType<_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
    name: lock.?name ?? 'lock-${name}'
    properties: {
      level: lock.?kind ?? ''
      notes: lock.?notes ?? (lock.?kind == 'CanNotDelete'
        ? 'Cannot delete resource or child resources.'
        : 'Cannot delete or modify the resource or child resources.')
    }
    scope: >singularMainResourceType<
  }
  
  lock: {
    kind: 'CanNotDelete'
    name: 'myCustomLockName'
    notes: 'This is a custom lock note.'
  }
  

Details on child and extension resources:

  • Locks SHOULD be able to be set for child resources of the primary resource in resource modules

Details on cross-referenced resources:

  • Locks MUST be automatically applied to cross-referenced resources if the primary resource has a lock applied.
    • This MUST also be able to be turned off for each of the cross-referenced resources by the module consumer via a parameter/variable if they desire

An example of this is a Key Vault module that has a Private Endpoints enabled. If a lock is applied to the Key Vault via the lock parameter/variable then the lock should also be applied to the Private Endpoint automatically, unless the privateEndpointLock/private_endpoint_lock (example name) parameter/variable is set to None

Tags

  @description('Optional. Tags of the resource.')
  param tags object?
  
  tags: {
    key: 'value'
    'another-key': 'another-value'
    integers: 123
  }
  

Details on child, extension and cross-referenced resources:

  • Tags MUST be automatically applied to child, extension and cross-referenced resources, if tags are applied to the primary resource.
    • By default, all tags set for the primary resource will automatically be passed down to child, extension and cross-referenced resources.
    • This MUST be able to be overridden by the module consumer so they can specify alternate tags for child, extension and cross-referenced resources, if they desire via a parameter/variable
      • If overridden by the module consumer, no merge/union of tags will take place from the primary resource and only the tags specified for the child, extension and cross-referenced resources will be applied

Managed Identities

  
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { managedIdentityAllType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. The managed identity definition for this resource.')
  param managedIdentities managedIdentityAllType?
  
  // ============= //
  //   Variables   //
  // ============= //
  
  var formattedUserAssignedIdentities = reduce(map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, (cur, next) => union(cur, next)) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
  var identity = !empty(managedIdentities) ? {
    type: (managedIdentities.?systemAssigned ?? false) ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null)
    userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
  } : null
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType< '>providerNamespace</>resourceType<@>apiVersion<' = {
    name: name
    identity: identity
    properties: {
      ... // other properties
    }
  }
  
  // =========== //
  //   Outputs   //
  // =========== //
  
  @description('The principal ID of the system assigned identity.')
  output systemAssignedMIPrincipalId string? = >singularMainResourceType<.?identity.?principalId
  
  managedIdentities: {
    systemAssigned: true
    userAssignedResourceIds: [
      '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}'
      '/subscriptions/{subscriptionId2}/resourceGroups/{resourceGroupName2}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName2}'
    ]
  }
  
  
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { managedIdentityOnlySysAssignedType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. The managed identity definition for this resource.')
  param managedIdentities managedIdentityOnlySysAssignedType?
  
  // ============= //
  //   Variables   //
  // ============= //
  
  var identity = !empty(managedIdentities)
    ? {
        type: (managedIdentities.?systemAssigned ?? false) ? 'SystemAssigned' : null
      }
    : null
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType< '>providerNamespace</>resourceType<@>apiVersion<' = {
    name: name
    identity: identity
    properties: {
      ... // other properties
    }
  }
  
  // =========== //
  //   Outputs   //
  // =========== //
  
  @description('The principal ID of the system assigned identity.')
  output systemAssignedMIPrincipalId string? = >singularMainResourceType<.?identity.?principalId
  
  managedIdentities: {
    systemAssigned: true
  }
  
  
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { managedIdentityOnlyUserAssignedType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. The managed identity definition for this resource.')
  param managedIdentities managedIdentityOnlyUserAssignedType?
  
  // ============= //
  //   Variables   //
  // ============= //
  
  var formattedUserAssignedIdentities = reduce(map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, (cur, next) => union(cur, next)) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
  var identity = !empty(managedIdentities)
    ? {
        type: !empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None'
        userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
      }
    : null
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType< '>providerNamespace</>resourceType<@>apiVersion<' = {
    name: name
    identity: identity
    properties: {
      ... // other properties
    }
  }
  
  managedIdentities: {
    userAssignedResourceIds: [
      '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}'
      '/subscriptions/{subscriptionId2}/resourceGroups/{resourceGroupName2}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName2}'
    ]
  }
  

Reason for differences in User Assigned data type in languages:

  • We do not foresee the Managed Identity Resource Provider team to ever add additional properties within the empty object ({}) value required on the input of a User Assigned Managed Identity.
  • In Bicep we therefore have removed the need for this to be declared and just converted it to a simple array of Resource IDs

Private Endpoints

Private Endpoints

E.g., for services that only have one private endpoint type.

  
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.')
  param privateEndpoints privateEndpointSingleServiceType[]?
  
  var enableReferencedModulesTelemetry = false // resource module
  
  // ============= //
  //   Resources   //
  // ============= //
  
  module >singularMainResourceType<_privateEndpoints 'br/public:avm/res/network/private-endpoint:>version<' = [for (privateEndpoint, index) in (privateEndpoints ?? []): {
    name: '${uniqueString(deployment().name, location)}->singularMainResourceType<-PrivateEndpoint-${index}'
    scope: resourceGroup(
      split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2],
      split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4]
    )
    params: {
      // Variant 1: A default service can be assumed (i.e., for services that only have one private endpoint type)
      name: privateEndpoint.?name ?? 'pep-${last(split(>singularMainResourceType<.id, '/'))}-${privateEndpoint.?service ?? '>defaultServiceName<'}-${index}'
      privateLinkServiceConnections: privateEndpoint.?isManualConnection != true ? [
        {
          name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(>singularMainResourceType<.id, '/'))}-${privateEndpoint.?service ?? '>defaultServiceName<'}-${index}'
          properties: {
            privateLinkServiceId: >singularMainResourceType<.id
            groupIds: [
              privateEndpoint.?service ?? '>defaultServiceName<'
            ]
          }
        }
      ] : null
      manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true ? [
        {
          name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(>singularMainResourceType<.id, '/'))}-${privateEndpoint.?service ?? '>defaultServiceName<'}-${index}'
          properties: {
            privateLinkServiceId: >singularMainResourceType<.id
            groupIds: [
              privateEndpoint.?service ?? '>defaultServiceName<'
            ]
            requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.'
          }
        }
      ] : null
      subnetResourceId: privateEndpoint.subnetResourceId
      enableTelemetry: enableReferencedModulesTelemetry // resource module
      enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry // pattern / utility module
      location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location
      lock: privateEndpoint.?lock ?? lock
      privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup
      roleAssignments: privateEndpoint.?roleAssignments
      tags: privateEndpoint.?tags ?? tags
      customDnsConfigs: privateEndpoint.?customDnsConfigs
      ipConfigurations: privateEndpoint.?ipConfigurations
      applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds
      customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName
    }
  }]
  
  @description('The private endpoints of the resource.')
  output privateEndpoints privateEndpointOutputType[] = [
    for (pe, index) in (privateEndpoints ?? []): {
      name: >singularMainResourceType<_privateEndpoints[index].outputs.name
      resourceId: >singularMainResourceType<_privateEndpoints[index].outputs.resourceId
      groupId: >singularMainResourceType<_privateEndpoints[index].outputs.?groupId!
      customDnsConfigs: >singularMainResourceType<_privateEndpoints[index].outputs.customDnsConfigs
      networkInterfaceResourceIds: >singularMainResourceType<_privateEndpoints[index].outputs.networkInterfaceResourceIds
    }
  ]
  
  // =============== //
  //   Definitions   //
  // =============== //
  
  @export()
  type privateEndpointOutputType = {
    @description('The name of the private endpoint.')
    name: string
  
    @description('The resource ID of the private endpoint.')
    resourceId: string
  
    @description('The group Id for the private endpoint Group.')
    groupId: string?
  
    @description('The custom DNS configurations of the private endpoint.')
    customDnsConfigs: {
      @description('FQDN that resolves to private endpoint IP address.')
      fqdn: string?
  
      @description('A list of private IP addresses of the private endpoint.')
      ipAddresses: string[]
    }[]
  
    @description('The IDs of the network interfaces associated with the private endpoint.')
    networkInterfaceResourceIds: string[]
  }
  
  privateEndpoints: {
    {
      name: 'myPeName'
      privateLinkServiceConnectionName: 'myPrivateLinkConnectionName'
      lock: 'CanNotDelete'
      tags: {
        'hidden-title': 'This is visible in the resource name'
      }
      subnetResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mysubnet'
      resourceGroupResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg'
      applicationSecurityGroupResourceIds: [
        '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.Network/applicationSecurityGroups/myAsg'
      ]
      privateDnsZoneGroup: {
        privateDnsZoneGroupConfigs: [
          {
            name: 'config'
            privateDnsZoneResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.Network/privateDnsZones/myZone'
          }
        ]
      }
      customDnsConfigs: [
        {
          fqdn: 'fqdn1.example.com'
          ipAddresses: [
            '10.0.0.1',
            '10.0.0.2'
          ]
        }
      ]
      networkInterfaceName: 'nic1'
      ipConfigurations: [
        {
          name: 'ipconfig1'
          groupId: 'vault'
          memberName: 'default'
          privateIpAddress: '10.0.0.7'
        }
      ]
      roleAssignments: [
        {
          roleDefinitionIdOrName: 'Owner'
          principalId: '11111111-1111-1111-1111-111111111111'
          principalType: 'ServicePrincipal'
        }
        {
          roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions','acdd72a7-3385-48ef-bd42-f606fba81ae7')
          principalId: '11111111-1111-1111-1111-111111111111'
          principalType: 'ServicePrincipal'
        }
      ]
    }
  }
  

E.g., for services that have more than one private endpoint type, like a Storage Account (blob, file, etc.)

  
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { privateEndpointMultiServiceType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.')
  param privateEndpoints privateEndpointMultiServiceType[]?
  
  var enableReferencedModulesTelemetry = false // resource module
  
  // ============= //
  //   Resources   //
  // ============= //
  
  module >singularMainResourceType<_privateEndpoints 'br/public:avm/res/network/private-endpoint:>version<' = [for (privateEndpoint, index) in (privateEndpoints ?? []): {
    name: '${uniqueString(deployment().name, location)}->singularMainResourceType<-PrivateEndpoint-${index}'
    scope: resourceGroup(
      split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[2],
      split(privateEndpoint.?resourceGroupResourceId ?? resourceGroup().id, '/')[4]
    )
    params: {
      // Variant 2: A default service cannot be assumed (i.e., for services that have more than one private endpoint type, like Storage Account)
      name: privateEndpoint.?name ?? 'pep-${last(split(>singularMainResourceType<.id, '/'))}-${privateEndpoint.service}-${index}'
      privateLinkServiceConnections: privateEndpoint.?isManualConnection != true ? [
        {
          name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(>singularMainResourceType<.id, '/'))}-${privateEndpoint.service}-${index}'
          properties: {
            privateLinkServiceId: >singularMainResourceType<.id
            groupIds: [
              privateEndpoint.service
            ]
          }
        }
      ] : null
      manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true ? [
        {
          name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(>singularMainResourceType<.id, '/'))}-${privateEndpoint.service}-${index}'
          properties: {
            privateLinkServiceId: >singularMainResourceType<.id
            groupIds: [
              privateEndpoint.service
            ]
            requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.'
          }
        }
      ] : null
      subnetResourceId: privateEndpoint.subnetResourceId
      enableTelemetry: enableReferencedModulesTelemetry // resource module
      enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry // pattern / utility module
      location: privateEndpoint.?location ?? reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location
      lock: privateEndpoint.?lock ?? lock
      privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup
      roleAssignments: privateEndpoint.?roleAssignments
      tags: privateEndpoint.?tags ?? tags
      customDnsConfigs: privateEndpoint.?customDnsConfigs
      ipConfigurations: privateEndpoint.?ipConfigurations
      applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds
      customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName
    }
  }]
  
  @description('The private endpoints of the resource.')
  output privateEndpoints privateEndpointOutputType[] = [
    for (pe, index) in (privateEndpoints ?? []): {
      name: >singularMainResourceType<_privateEndpoints[index].outputs.name
      resourceId: >singularMainResourceType<_privateEndpoints[index].outputs.resourceId
      groupId: >singularMainResourceType<_privateEndpoints[index].outputs.?groupId!
      customDnsConfigs: >singularMainResourceType<_privateEndpoints[index].outputs.customDnsConfigs
      networkInterfaceResourceIds: >singularMainResourceType<_privateEndpoints[index].outputs.networkInterfaceResourceIds
    }
  ]
  
  // =============== //
  //   Definitions   //
  // =============== //
  
  @export()
  type privateEndpointOutputType = {
    @description('The name of the private endpoint.')
    name: string
  
    @description('The resource ID of the private endpoint.')
    resourceId: string
  
    @description('The group Id for the private endpoint Group.')
    groupId: string?
  
    @description('The custom DNS configurations of the private endpoint.')
    customDnsConfigs: {
      @description('FQDN that resolves to private endpoint IP address.')
      fqdn: string?
  
      @description('A list of private IP addresses of the private endpoint.')
      ipAddresses: string[]
    }[]
  
    @description('The IDs of the network interfaces associated with the private endpoint.')
    networkInterfaceResourceIds: string[]
  }
  
  privateEndpoints: {
    {
      name: 'myPeName'
      privateLinkServiceConnectionName: 'myPrivateLinkConnectionName'
      lock: 'CanNotDelete'
      tags: {
        'hidden-title': 'This is visible in the resource name'
      }
      service: 'blob'
      subnetResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mysubnet'
      resourceGroupResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg'
      applicationSecurityGroupResourceIds: [
        '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.Network/applicationSecurityGroups/myAsg'
      ]
      privateDnsZoneGroup: {
        privateDnsZoneGroupConfigs: [
          {
            name: 'config'
            privateDnsZoneResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.Network/privateDnsZones/myZone'
          }
        ]
      }
      customDnsConfigs: [
        {
          fqdn: 'fqdn1.example.com'
          ipAddresses: [
            '10.0.0.1',
            '10.0.0.2'
          ]
        }
      ]
      networkInterfaceName: 'nic1'
      ipConfigurations: [
        {
          name: 'ipconfig1'
          groupId: 'blob'
          memberName: 'default'
          privateIpAddress: '10.0.0.7'
        }
      ]
      roleAssignments: [
        {
          roleDefinitionIdOrName: 'Owner'
          principalId: '11111111-1111-1111-1111-111111111111'
          principalType: 'ServicePrincipal'
        }
        {
          roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions','acdd72a7-3385-48ef-bd42-f606fba81ae7')
          principalId: '11111111-1111-1111-1111-111111111111'
          principalType: 'ServicePrincipal'
        }
      ]
    }
  }
  

Notes:

  • The properties defined in the schema above are the minimum amount of properties expected to be exposed for Private Endpoints in AVM Resource Modules.
    • A module owner MAY chose to expose additional properties of the Private Endpoint resource
      • However, module owners considering this SHOULD contact the AVM core team first to consult on how the property should be exposed to avoid future breaking changes to the schema that may be enforced upon them
  • Module owners MAY chose to define a list of allowed value for the ‘service’ (a.k.a. groupIds) property
    • However, they should do so with caution as should a new service appear for their resource module, a new release will need to be cut to add this new service to the allowed values
      • Whereas not specifying allowed values will allow flexibility from day 0 without the need for any changes and releases to be made

Customer Managed Keys

Customer Managed Keys
  // ============== //
  //   Parameters   //
  // ============== //
  
  import { customerManagedKeyType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. The customer managed key definition.')
  param customerManagedKey customerManagedKeyType?
  
  // ============= //
  //   Variables   //
  // ============= //
  
  // If user-assiged identities are supported => Adds any user assigned identity specified in the customer managed key definition to the general managed-identity spcification
  var formattedUserAssignedIdentities = reduce(
    map(
      union(
        (managedIdentities.?userAssignedResourceIds ?? []),
        (!empty(customerManagedKey.?userAssignedIdentityResourceId)
          ? [customerManagedKey.?userAssignedIdentityResourceId]
          : [])
      ),
      (id) => { '${id}': {} }
    ),
    {},
    (cur, next) => union(cur, next)
  ) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
  
  var identity = !empty(managedIdentities) || !empty(formattedUserAssignedIdentities) 
    ? {
        type: (managedIdentities.?systemAssigned ?? false)
          ? (!empty(formattedUserAssignedIdentities) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned')
          : (!empty(formattedUserAssignedIdentities) ? 'UserAssigned' : null)
        userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
      }
    : null
  
  var isHSMManagedCMK = split(customerManagedKey.?keyVaultResourceId ?? '', '/')[?7] == 'managedHSMs'
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource cMKKeyVault 'Microsoft.KeyVault/vaults@2025-05-01' existing = if (!empty(customerManagedKey) && !isHSMManagedCMK) {
    name: last(split((customerManagedKey!.?keyVaultResourceId!), '/'))
    scope: resourceGroup(
      split(customerManagedKey!.?keyVaultResourceId!, '/')[2],
      split(customerManagedKey!.?keyVaultResourceId!, '/')[4]
    )
  
    resource cMKKey 'keys@2025-05-01' existing = if (!empty(customerManagedKey) && !isHSMManagedCMK) {
      name: customerManagedKey!.?keyName!
    }
  }
  
  resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) {
    name: last(split(customerManagedKey!.?userAssignedIdentityResourceId!, '/'))
    scope: resourceGroup(
      split(customerManagedKey!.?userAssignedIdentityResourceId!, '/')[2],
      split(customerManagedKey!.?userAssignedIdentityResourceId!, '/')[4]
    )
  }
  
  resource >singularMainResourceType< '>providerNamespace</>resourceType<@>apiVersion<' = {
    name: '>exampleResource<'
    properties: {
      ... // other properties
      encryption: !empty(customerManagedKey)
        ? {
            keySource: 'Microsoft.KeyVault'
            keyVaultProperties: {
              keyVaultUri: !isHSMManagedCMK
                  ? cMKKeyVault!.properties.vaultUri
                  : 'https://${last(split((customerManagedKey!.keyVaultResourceId), '/'))}.managedhsm.azure.net/'
              keyName: customerManagedKey!.keyName
              keyVersion: !empty(customerManagedKey!.?keyVersion)
                ? customerManagedKey!.keyVersion!
                : (!isHSMManagedCMK
                  ? last(split(cMKKeyVault::cMKKey!.properties.keyUriWithVersion, '/'))
                  : fail('Managed HSM CMK encryption requires specifying the \'keyVersion\'.'))
              keyIdentifier: !empty(customerManagedKey!.?keyVersion)
                ? ( !isHSMManagedCMK
                  ? '${cMKKeyVault::cMKKey!.properties.keyUri}/${customerManagedKey!.keyVersion!}'
                  : 'https://${last(split((customerManagedKey!.keyVaultResourceId), '/'))}.managedhsm.azure.net/keys/${customerManagedKey!.keyName}/${customerManagedKey!.keyVersion!}')
                : ( !isHSMManagedCMK
                  ? cMKKeyVault::cMKKey!.properties.keyUriWithVersion
                  : fail('Managed HSM CMK encryption requires specifying the \'keyVersion\'.'))
              identityClientId: !empty(customerManagedKey!.?userAssignedIdentityResourceId)
                ? cMKUserAssignedIdentity!.properties.clientId
                : null
              identity: !empty(customerManagedKey!.?userAssignedIdentityResourceId)
                ? {
                    userAssignedIdentity: cMKUserAssignedIdentity!.id
                  }
                : null
            }
          }
        : null
    }
  }
  
  customerManagedKey: {
    keyVaultResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{keyVaultName}'
    keyName: '{keyName}'
    keyVersion: '{keyVersion}'
    userAssignedIdentityResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{uamiName}'
  }
  
  // ============== //
  //   Parameters   //
  // ============== //
  import { customerManagedKeyWithAutoRotateType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Optional. The customer managed key definition.')
  param customerManagedKey customerManagedKeyWithAutoRotateType?
  
  // ============= //
  //   Variables   //
  // ============= //
  
  // If user-assiged identities are supported => Adds any user assigned identity specified in the customer managed key definition to the general managed-identity spcification
  var formattedUserAssignedIdentities = reduce(
    map(
      union(
        (managedIdentities.?userAssignedResourceIds ?? []),
        (!empty(customerManagedKey.?userAssignedIdentityResourceId)
          ? [customerManagedKey.?userAssignedIdentityResourceId]
          : [])
      ),
      (id) => { '${id}': {} }
    ),
    {},
    (cur, next) => union(cur, next)
  ) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
  
  var identity = !empty(managedIdentities) || !empty(formattedUserAssignedIdentities) 
    ? {
        type: (managedIdentities.?systemAssigned ?? false)
          ? (!empty(formattedUserAssignedIdentities) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned')
          : (!empty(formattedUserAssignedIdentities) ? 'UserAssigned' : null)
        userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
      }
    : null
    
  var isHSMManagedCMK = split(customerManagedKey.?keyVaultResourceId ?? '', '/')[?7] == 'managedHSMs'
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource cMKKeyVault 'Microsoft.KeyVault/vaults@2025-05-01' existing = if (!empty(customerManagedKey) && !isHSMManagedCMK) {
    name: last(split((customerManagedKey!.?keyVaultResourceId!), '/'))
    scope: resourceGroup(
      split(customerManagedKey!.?keyVaultResourceId!, '/')[2],
      split(customerManagedKey!.?keyVaultResourceId!, '/')[4]
    )
  
    resource cMKKey 'keys@2025-05-01' existing = if (!empty(customerManagedKey) && !isHSMManagedCMK) {
      name: customerManagedKey!.?keyName!
    }
  }
  
  resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' existing = if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) {
    name: last(split(customerManagedKey!.?userAssignedIdentityResourceId!, '/'))
    scope: resourceGroup(
      split(customerManagedKey!.?userAssignedIdentityResourceId!, '/')[2],
      split(customerManagedKey!.?userAssignedIdentityResourceId!, '/')[4]
    )
  }
  
  resource >singularMainResourceType< '>providerNamespace</>resourceType<@>apiVersion<' = {
    name: '>exampleResource<'
    properties: {
      ... // other properties
      encryption: !empty(customerManagedKey)
        ? {
            keySource: 'Microsoft.KeyVault'
            keyVaultProperties: {
              keyVaultUri: !isHSMManagedCMK
                  ? cMKKeyVault!.properties.vaultUri
                  : 'https://${last(split((customerManagedKey!.keyVaultResourceId), '/'))}.managedhsm.azure.net/'
              keyName: customerManagedKey!.keyName
              keyVersion: !empty(customerManagedKey!.?keyVersion)
                  ? customerManagedKey!.keyVersion!
                  : (customerManagedKey!.?autoRotationEnabled ?? true)
                      ? null
                      : (!isHSMManagedCMK
                          ? last(split(cMKKeyVault::cMKKey!.properties.keyUriWithVersion, '/'))
                          : fail('Managed HSM CMK encryption requires either specifying the \'keyVersion\' or omitting the \'autoRotationEnabled\' property. Setting \'autoRotationEnabled\' to false without a \'keyVersion\' is not allowed.'))
              keyIdentifier: !empty(customerManagedKey!.?keyVersion)
                ? (!isHSMManagedCMK
                  ? '${cMKKeyVault::cMKKey!.properties.keyUri}/${customerManagedKey!.keyVersion!}'
                  : 'https://${last(split((customerManagedKey!.keyVaultResourceId), '/'))}.managedhsm.azure.net/keys/${customerManagedKey!.keyName}/${customerManagedKey!.keyVersion!}')
                : (customerManagedKey!.?autoRotationEnabled ?? true)
                  ? (!isHSMManagedCMK
                    ? cMKKeyVault::cMKKey!.properties.keyUri
                    : 'https://${last(split((customerManagedKey!.keyVaultResourceId), '/'))}.managedhsm.azure.net/keys/${customerManagedKey!.keyName}')
                  : (!isHSMManagedCMK
                    ? cMKKeyVault::cMKKey!.properties.keyUriWithVersion
                    : fail('Managed HSM CMK encryption requires either specifying the \'keyVersion\' or omitting the \'autoRotationEnabled\' property. Setting \'autoRotationEnabled\' to false without a \'keyVersion\' is not allowed.'))
              identityClientId: !empty(customerManagedKey!.?userAssignedIdentityResourceId)
                ? cMKUserAssignedIdentity!.properties.clientId
                : null
              identity: !empty(customerManagedKey!.?userAssignedIdentityResourceId)
                ? {
                    userAssignedIdentity: cMKUserAssignedIdentity!.id
                  }
                : null
            }
          }
        : null
    }
  }
  
  customerManagedKey: {
    keyVaultResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{keyVaultName}'
    keyName: '{keyName}'
    autoRotationEnabled: {true|false}
    userAssignedIdentityResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{uamiName}'
  }
  

Secrets export (DEPRECATED)

Important

Since version Bicep 0.35.1, it is possible to export secrets securely using the secure() annotation.

As this approach is fairly simple compared with the below workaround it is highly recommended to use it instead.

Example

@secure()
@description('The primary connection string of the service bus namespace.')
output primaryConnectionString string = listkeys(
  '${serviceBusNamespace.id}/AuthorizationRules/RootManageSharedAccessKey',
  '2024-01-01'
).primaryConnectionString

@secure()
@description('The primary key of the service bus namespace.')
output primaryKey string = listkeys(
  '${serviceBusNamespace.id}/AuthorizationRules/RootManageSharedAccessKey',
  '2024-01-01'
).primaryKey

Secrets used inside a module can be exported to a Key Vault reference provided as per the below schema.
This implementation provides a secure way around the current limitation of Bicep on providing a secure template output (that can be used for secrets).

The user MUST

  • provide the resource Id to a Key Vault. The principal used for the deployment MUST be allowed to set secrets in this Key Vault.
  • provide a name for each secret they want to store (opt-in). The module will suggest which secrets are available via the implemented user-defined type.

The module returns an output table where the key is the name of the secret the user provided, and the value contains both the secret’s resource Id and URI.

Important

The feature MUST be implemented as per the below schema. Diversions are only allowed in places marked as >text< to ensure a consistent user experience across modules.

User Defined Type, Parameter & Resource Example

  
  // ============== //
  //   Parameters   //
  // ============== //
  
  @description('Optional. Key vault reference and secret settings for the module\'s secrets export.')
  param secretsExportConfiguration secretsExportConfigurationType?
  
  // ============= //
  //   Resources   //
  // ============= //
  
  module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) {
    name: '${uniqueString(deployment().name, location)}-secrets-kv'
    scope: resourceGroup(
      split(secretsExportConfiguration.?keyVaultResourceId, '/')[2],
      split(secretsExportConfiguration.?keyVaultResourceId, '/')[4]
    )
    params: {
      keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId, '/'))
      secretsToSet: union(
        [],
        contains(secretsExportConfiguration!, '>secretToExport1<Name')
          ? [
              {
                name: secretsExportConfiguration!.?>secretToExport1<Name
                value: >secretReference1< // e.g., >singularMainResourceType<.listKeys().primaryMasterKey
              }
            ]
          : [],
        contains(secretsExportConfiguration!, '>secretToExport2<Name')
          ? [
              {
                name: secretsExportConfiguration!.?>secretToExport2<Name
                value:>secretReference2<  // e.g., >singularMainResourceType<.listKeys().secondaryMasterKey
              }
            ]
          : []
          // (...)
      )
    }
  }
  
  // =========== //
  //   Outputs   //
  // =========== //
  
  import { secretsOutputType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret\'s name.')
  output exportedSecrets secretsOutputType = (secretsExportConfiguration != null)
    ? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret)
    : {}
  
  // =============== //
  //   Definitions   //
  // =============== //
  
  @export()
  type secretsExportConfigurationType = {
    @description('Required. The resource ID of the key vault where to store the secrets of this module.')
    keyVaultResourceId: string
  
    @description('Optional. The >secretToExport1< secret name to create.')
    >secretToExport1<Name: string?
  
    @description('Optional. The >secretToExport2< secret name to create.')
    >secretToExport2<Name: string?
  
    // (...)
  }
  

Input Example with Values

  
  // ============== //
  //   Parameters   //
  // ============== //
  
  @description('Optional. Key vault reference and secret settings for the module\'s secrets export.')
  param secretsExportConfiguration secretsExportConfigurationType?
  
  // ============= //
  //   Resources   //
  // ============= //
  
  module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) {
    name: '${uniqueString(deployment().name, location)}-secrets-kv'
    scope: resourceGroup(
      split(secretsExportConfiguration.?keyVaultResourceId, '/')[2],
      split(secretsExportConfiguration.?keyVaultResourceId, '/')[4]
    )
    params: {
      keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId, '/'))
      secretsToSet: union(
        [],
        contains(secretsExportConfiguration!, '>secretToExport1<Name')
          ? [
              {
                name: secretsExportConfiguration!.?>secretToExport1<Name
                value: >secretReference1< // e.g., >singularMainResourceType<.listKeys().primaryMasterKey
              }
            ]
          : [],
        contains(secretsExportConfiguration!, '>secretToExport2<Name')
          ? [
              {
                name: secretsExportConfiguration!.?>secretToExport2<Name
                value:>secretReference2<  // e.g., >singularMainResourceType<.listKeys().secondaryMasterKey
              }
            ]
          : []
          // (...)
      )
    }
  }
  
  // =========== //
  //   Outputs   //
  // =========== //
  
  import { secretsOutputType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret\'s name.')
  output exportedSecrets secretsOutputType = (secretsExportConfiguration != null)
    ? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret)
    : {}
  
  // =============== //
  //   Definitions   //
  // =============== //
  
  @export()
  type secretsExportConfigurationType = {
    @description('Required. The resource ID of the key vault where to store the secrets of this module.')
    keyVaultResourceId: string
  
    @description('Optional. The >secretToExport1< secret name to create.')
    >secretToExport1<Name: string?
  
    @description('Optional. The >secretToExport2< secret name to create.')
    >secretToExport2<Name: string?
  
    // (...)
  }
  

[modules/keyVaultExport.bicep] file

  
  // ============== //
  //   Parameters   //
  // ============== //
  
  @description('Required. The name of the Key Vault to set the secrets in.')
  param keyVaultName string
  
  import { secretToSetType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('Required. The secrets to set in the Key Vault.')
  param secretsToSet secretToSetType[]
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
    name: keyVaultName
  }
  
  resource secrets 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = [
    for secret in secretsToSet: {
      name: secret.name
      parent: keyVault
      properties: {
        value: secret.value
      }
    }
  ]
  
  // =========== //
  //   Outputs   //
  // =========== //
  
  import { secretSetOutputType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('The references to the secrets exported to the provided Key Vault.')
  output secretsSet secretSetOutputType[] = [
    #disable-next-line outputs-should-not-contain-secrets // Only returning the references, not a secret value
    for index in range(0, length(secretsToSet ?? [])): {
      secretResourceId: secrets[index].id
      secretUri: secrets[index].properties.secretUri
      secretUriWithVersion: secrets[index].properties.secretUriWithVersion
    }
  ]
  

Output Usage Example

When using a module that implements the above interface, you can access its outputs for example in the following ways:

  
  // ============== //
  //   Parameters   //
  // ============== //
  
  @description('Optional. Key vault reference and secret settings for the module\'s secrets export.')
  param secretsExportConfiguration secretsExportConfigurationType?
  
  // ============= //
  //   Resources   //
  // ============= //
  
  module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) {
    name: '${uniqueString(deployment().name, location)}-secrets-kv'
    scope: resourceGroup(
      split(secretsExportConfiguration.?keyVaultResourceId, '/')[2],
      split(secretsExportConfiguration.?keyVaultResourceId, '/')[4]
    )
    params: {
      keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId, '/'))
      secretsToSet: union(
        [],
        contains(secretsExportConfiguration!, '>secretToExport1<Name')
          ? [
              {
                name: secretsExportConfiguration!.?>secretToExport1<Name
                value: >secretReference1< // e.g., >singularMainResourceType<.listKeys().primaryMasterKey
              }
            ]
          : [],
        contains(secretsExportConfiguration!, '>secretToExport2<Name')
          ? [
              {
                name: secretsExportConfiguration!.?>secretToExport2<Name
                value:>secretReference2<  // e.g., >singularMainResourceType<.listKeys().secondaryMasterKey
              }
            ]
          : []
          // (...)
      )
    }
  }
  
  // =========== //
  //   Outputs   //
  // =========== //
  
  import { secretsOutputType } from 'br/public:avm/utl/types/avm-common-types:>version<'
  @description('A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret\'s name.')
  output exportedSecrets secretsOutputType = (secretsExportConfiguration != null)
    ? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret)
    : {}
  
  // =============== //
  //   Definitions   //
  // =============== //
  
  @export()
  type secretsExportConfigurationType = {
    @description('Required. The resource ID of the key vault where to store the secrets of this module.')
    keyVaultResourceId: string
  
    @description('Optional. The >secretToExport1< secret name to create.')
    >secretToExport1<Name: string?
  
    @description('Optional. The >secretToExport2< secret name to create.')
    >secretToExport2<Name: string?
  
    // (...)
  }
  

Which returns a JSON-formatted output like:

  {
    "exportedSecrets": {
      "Type": "Object",
      "Value": {
        ">secretToExportName1<": {
          "secretResourceId": "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName1<",
          "secretUri": "https://<vaultName>.vault.azure.net/secrets/>secretToExportName1<"
        },
        ">secretToExportName2<": {
          "secretResourceId": "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName2<",
          "secretUri": "https://<vaultName>.vault.azure.net/secrets/>secretToExportName2<"
        }
      }
    },
    "specificSecret": {
      "Type": "String",
      "Value": "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName1<"
    },
    "exportedSecretResourceIds": {
      "Type": "Array",
      "Value": [
        "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName1<",
        "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName2<"
      ]
    }
  }
  

Azure Monitor Alerts

Note

This interface is a SHOULD instead of a MUST and therefore the AVM core team have not mandated a interface schema to use.

Zonal & zone-redundant resources

Many Azure resources can be deployed into specific availability zones. Depending on whether a resource is ‘zonal’ (i.e., deploys a single instance into a single zone) or ‘zone-redundant’ (i.e., spreads multiple of its instances across the configured zones), implementing a different interface is required. Simply put, the zone of a zonal resource must be a required parameter (but give the user the option to ‘opt-out’), while zone-redundant resources must span all available zones by default, but still give the user the option to ‘opt-out’. Please note that the support for Availability Zones may differ from region to region.

  // ============== //
  //   Parameters   //
  // ============== //
  
  @description('Required. If set to 1, 2 or 3, the availability zone is hardcoded to that value. If set to -1, no zone is defined. Note that the availability zone numbers here are the logical availability zone in your Azure subscription. Different subscriptions might have a different mapping of the physical zone and logical zone. To understand more, please refer to [Physical and logical availability zones](https://learn.microsoft.com/en-us/azure/reliability/availability-zones-overview?tabs=azure-cli#physical-and-logical-availability-zones).')
  @allowed([
    -1
    1
    2
    3
  ])
  param availabilityZone int
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType< '>providerNamespace</>resourceType<@>apiVersion<' = {
    name: '>exampleResource<'
    properties: {
      ... // other properties
      zones: availabilityZone != -1 ? array(string(availabilityZone)) : null // If expecting an array
      // Or
      availabilityZone: availabilityZone != -1 ? string(availabilityZone) : null // If expecting a single value
    }
  }
  
  availabilityZone: -1 // Deploy into no zone
  availabilityZone: 1 // Deploy into zone 1
  
  // ============== //
  //   Parameters   //
  // ============== //
  
  @description('Optional. The list of Availability zones to use for the zone-redundant resources.')
  @allowed([
    1
    2
    3
  ])
  param availabilityZones int[] = [1, 2, 3]
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource >singularMainResourceType< '>providerNamespace</>resourceType<@>apiVersion<' = {
    name: '>exampleResource<'
    properties: {
      ... // other properties
      zones: map(availabilityZones, zone => '${zone}')
    }
  }
  
  availabilityZones: [] // Deploy into no zone
  availabilityZones: [1, 2] // Deploy into zone 1 & 2
  

Bicep Pattern Module Specifications

Contribution / Support

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR8Module Owner(s) GitHubMUSTOwnerInitial
2SNFR20GitHub Teams OnlyMUSTOwnerInitial
3SNFR9AVM & PG Teams GitHub Repo PermissionsMUSTOwnerInitial
4SNFR10MIT LicensingMUSTOwnerInitial
5SNFR11Issues Response TimesMUSTOwnerContributorBAU
6SNFR12Versions SupportedMUSTOwnerBAU
7SNFR23GitHub Repo LabelsMUSTOwnerBAU
8BCPNFR15AVM Module Issue template fileMUSTOwnerBAU
➕ See Specifications for this category
See origin...

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.

Note

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.




See origin...

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 a GitHub team assigned for module owners. This team MUST be created in the Azure organization in GitHub.

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

Info

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.

Bicep

Important

As part of the module proposal process, the name of the GitHub team for each approved module is already defined in the respective Module Indexes (or CSV file). This team MUST be created (and used) for each module.

Module owners don’t need to construct the name of the GitHub team for their module themselves, instead they need use the name prescribed in the related CSV file, at the time of approval.

For a direct link, see the list of related index pages:

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!

Naming Convention

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

  • <hyphenated module name>-module-owners-bicep - to grant permissions for module owners on Bicep modules

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 == the role the GitHub Team is assigned to
  • <bicep == the language the module is written in

Examples:

  • avm-res-compute-virtualmachine-module-owners-bicep
Note

The naming convention for Bicep modules is slightly different than the naming convention for their respective GitHub teams.

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.

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

Grant permissions through team memberships

Note

In case of Bicep modules, permissions to the BRM repository (the repo of the Bicep Registry) are granted via assigning the -module-owners- 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 team to the respective parent, only the AVM core team can approve this parent-child relationship.

Module owners MUST create their -module-owners- team and as part of the provisioning process, they MUST request the addition of this team to its respective parent team (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.

Example - GitHub team 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.
Tip

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).

Note

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 Bicep reviewers, with the @Azure/ prefix, i.e., @Azure/avm-module-reviewers-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-module-reviewers-bicep

Terraform

Note

Access management for Terraform repositories now uses a single team, membership of which is managed using an internal entitlement management tool (Core Identity).

All module owners MUST request access to the avm-module-owners-terraform GitHub team via the Azure Verified Module Owners Terraform entitlement in Core Identity (Microsoft internal tool).




See origin...

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

Note

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

Terraform

Important

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.




See origin...

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

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




See origin...

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

A module owner MUST respond to logged issues as defined in the support statement. See Module Support for more information.




See origin...

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.




See origin...

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:

➕ AVM Standard GitHub 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 📣In the AVM repository: this module needs an owner to develop or maintain it. In the BRM repository: the module owner needs to review a PR.
FF0019
Needs: Module Contributor 📣This module needs secondary owner(s) or contributor(s) to develop or maintain it
C95474
Needs: Core Team 🧞‍♂️This item needs the AVM Core Team to review it
DB4503
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 Deprecated 🔴This is a request to deprecate a module
000000
Status: Module Orphaned 🟡The module has no owner and is therefore orphaned at this time
F4A460
Status: Ready For Repository Creation 📝This module is approved and the owner is ready for the repository to be created (Terraform)
136A41
Status: Repository Created 📄This module has had it's repository created and configured ready for owner contribution (Terraform)
27AB03
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: CI 🚀This issue is related to the AVM CI
74CFB0
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
Class: Utility Module 📦This is a utility module
CAD1DE
Class: Child Module 📦This is a child module
5E5186

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

➕ Set-AvmGitHubLabels.ps1

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
```shell
# 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
  



See origin...

ID: BCPNFR15 - Category: Contribution/Support - AVM Module Issue template file

Module owners MUST add an entry to the AVM Module Issue template file in the BRM repository (here). When the module is deprecated, this entry MUST be removed from the file.

Note

Through this approach, the AVM core team will allow raising a bug or feature request for a module, only after the module gets merged to the BRM repository.

The module name entry MUST be added to the dropdown list with id module-name-dropdown as an option, in alphabetical order.

Important

Module owners MUST ensure that the module name is added in alphabetical order, to simplify selecting the right module name when raising an AVM module issue.

Example - AVM Module Issue template module name entry for the Bicep resource module of Azure Virtual Network (avm/res/network/virtual-network):

- type: dropdown
  id: module-name-dropdown
  attributes:
    label: Module Name
    description: Which existing AVM module is this issue related to?
    options:
      ...
      - "avm/res/network/virtual-network"
      ...



Telemetry

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR3Deployment/Usage TelemetryMUSTOwnerInitial
2SFR4Telemetry Enablement FlexibilityMUSTOwnerInitial
3BCPFR4Telemetry EnablementMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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

Modules MUST provide the capability to collect deployment/usage telemetry as detailed in Telemetry further.

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.)

Telemetry Information Notice

Note

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

  • Bicep: Using the 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.

Module Class Applicability

This specification applies to all AVM module classes (resource, pattern, utility), however, in case of utility modules, telemetry collection MUST only be added when the utility module deploys any resources (e.g., a deployment script resource). If the utility module does not deploy any resources, telemetry collection MUST NOT be added.

Bicep

Important

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.

The value you need to use for your module is defined in the related module index. You can look it up on the index pages for Resource Modules, Pattern Modules and Utility Modules.

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

  • <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-
    • The prefixes: avm-ptn-
  • <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)
Note

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.

An example deployment name for the AVM Virtual Machine Resource Module would be: 46d3xbcp.res.compute-virtualmachine.1-2-3.eum3

An example deployment name for a shortened module name would be: 46d3xbcp.res.desktopvirtualization-appgroup.1-2-3.eum3

Tip

Terraform: Terraform uses a telemetry provider, the configuration of which is the same for every module and is included in the template repo.

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

Terraform

To enable telemetry data collection for Terraform modules, the modtm telemetry provider MUST be used. 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.

The modtm provider MUST be 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"
    }
  }
}



See origin...

ID: SFR4 - Category: Telemetry - Telemetry Enablement Flexibility

The telemetry collection MUST be on/enabled by default, however module consumers MUST be allowed to disable it by setting the below parameter/variable value to false:

  • Bicep: enableTelemetry
  • Terraform: enable_telemetry
Note

Whenever a module references AVM modules that implement the telemetry parameter (e.g., a pattern module that uses AVM resource modules), the telemetry parameter value MUST be passed through to these modules. This is necessary to ensure a consumer can reliably enable & disable the telemetry feature for all used modules.

This general specification can be modified for some use-cases, that are language specific:

Bicep

For cross-references in resource modules, the spec BCPFR7 also applies.

Terraform

Currently, no further requirements apply.




See origin...

ID: BCPFR4 - Category: Composition - Telemetry Enablement

To comply with specifications outlined in SFR3 & SFR4 you MUST incorporate the following code snippet into your modules. Place this code sample in the “top level” main.bicep file; it is not necessary to include it in any nested Bicep files (child modules), unless they are marked for direct publishing (Ref Child module publishing).

@description('Optional. Location for all resources.')
param location string = resourceGroup().location

@description('Optional. Enable/Disable usage telemetry for module.')
param enableTelemetry bool = true

#disable-next-line no-deployments-resources
resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) {
  name: take('46d3xbcp.res.compute-virtualmachine.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}', 64)
  properties: {
    mode: 'Incremental'
    template: {
      '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
      contentVersion: '1.0.0.0'
      resources: []
      outputs: {
        telemetry: {
          type: 'String'
          value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
        }
      }
    }
  }
}



Naming / Composition

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR1Preview ServicesMUSTOwnerBAU
2SFR2WAF AlignedSHOULDOwnerBAU
3SFR5Availability ZonesMUSTOwnerInitial
4SFR6Data RedundancyMUSTOwnerInitial
5SNFR25Resource NamingMUSTOwnerInitial
6PMFR1Resource Group CreationMAYOwnerContributorBAU
7PMNFR1Module NamingMUSTOwnerInitial
8PMNFR2Use Resource Modules to Build a Pattern ModuleMUSTOwnerContributorBAU
9PMNFR3Use other Pattern Modules to Build a Pattern ModuleMUSTOwnerContributorBAU
10BCPFR1Cross-Referencing ModulesMAYOwnerContributorBAU
11BCPFR2Role Assignments Role Definition MappingMUSTOwnerContributorBAU
12BCPFR6Cross-Referencing Child-ModulesMUSTOwnerContributorBAU
13BCPNFR19User-defined types - NamingMUSTOwnerContributorBAU
14BCPNFR23Module compositionMUSTOwnerContributorBAU
15BCPNFR24Deterministic Deployment NamesMUSTOwnerContributorBAU
16BCPNFR5Role Assignments Role Definition Mapping LimitsSHOULDOwnerContributorBAU
17BCPNFR6Role Assignments Role Definition Mapping Compulsory RolesMUSTOwnerContributorBAU
18BCPNFR14VersioningMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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”



See origin...

ID: SFR2 - Category: Composition - WAF Aligned

Modules SHOULD set defaults in input parameters/variables to align to high priority/impact/severity 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.

Tip

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




See origin...

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.

Note

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




See origin...

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.

Note

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




See origin...

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>.

Tip

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.




See origin...

ID: PMFR1 - Category: Composition - Resource Group Creation

A Pattern Module MAY create Resource Group(s).




See origin...

ID: PMNFR1 - Category: Naming - Module Naming

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

Important

As part of the module proposal process, the module’s approved name is captured both in the module proposal issue AND the related module index page (backed by the corresponding CSV file).

Therefore, module owners don’t need to construct the module’s name themselves, instead they need use the name prescribed in the module proposal issue or in the related CSV file, at the time of approval.

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



See origin...

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.
Note

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.




See origin...

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.




See origin...

ID: BCPFR1 - Category: Composition - Cross-Referencing Modules

Module owners MAY cross-reference other modules to build either Resource or Pattern modules.

However, they MUST be referenced only by a public registry reference to a pinned version e.g. br/public:avm/[res|ptn|utl]/<publishedModuleName>:>version<. They MUST NOT use local parent path references to a module e.g. ../../xxx/yyy.bicep.

The only exception to this rule are child modules as documented in BCPFR6.

Modules MUST NOT contain references to non-AVM modules.




See origin...

ID: BCPFR2 - Category: Composition - Role Assignments Role Definition Mapping

Module owners MAY define common RBAC Role Definition names and IDs within a variable to allow consumers to define a RBAC Role Definition by their name rather than their ID, this should be self contained within the module themselves.

However, they MUST use only the official RBAC Role Definition name within the variable and nothing else.

To meet the requirements of BCPFR2, BCPNFR5 and BCPNFR6 you MUST use the below code sample in your AVM Modules to achieve this.

  @description('''Required. You can provide either the display name (note not all roles are supported, check module documentation) of the role definition, or its fully qualified ID in the following format: `/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11`.''')
  param roleDefinitionIdOrName string
  
  var builtInRbacRoleNames = {
    Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
    Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c'
    Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7'
    'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168'
    'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'
    //Other RBAC Role Definitions Names & IDs can be added here as needed for your module
  }
  
  var roleDefinitionIdMappedResult = (contains(builtInRbacRoleNames, roleDefinitionIdOrName) ? builtInRbacRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName)
  
  resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
    //Other properties removed for ease of reading
    properties: {
      roleDefinitionId: roleDefinitionIdMappedResult
      //Other properties removed for ease of reading
    }
  }
  



See origin...

ID: BCPFR6 - Cross-Referencing Child-Modules

Parent templates MUST reference all their direct child-templates to allow for an end-to-end deployment experience.
For example, the SQL server template must reference its child database module and encapsulate it in a loop to allow for the deployment of multiple databases.

@description('Optional. The databases to create in the server')
param databases databaseType[]?

resource server 'Microsoft.Sql/servers@(...)' = { (...) }

module server_databases 'database/main.bicep' = [for (database, index) in (databases ?? []): {
  name: '${uniqueString(server.id, location)}-Sql-DB-${index}'
  params: {
    serverName: server.name
    (...)
  }
}]



See origin...

ID: BCPNFR19 - User-defined types - Naming

User-defined types (UDTs) MUST always end with the suffix (...)Type to make them obvious to users. In addition it is recommended to extend the suffix to (...)OutputType if a UDT is exclusively used for outputs.

type subnet = { ... } // Wrong
type subnetType = { ... } // Correct
type subnetOutputType = { ... } // Correct, if used only for outputs

Since User-defined types (UDTs) MUST always be singular as per BCPNFR18, their naming should reflect this and also be singular.

type subnetsType = { ... } // Wrong
type subnetType = { ... } // Correct



See origin...

ID: BCPNFR23 - Category: Composition

Each Bicep AVM module that lives within the Azure/bicep-registry-modules (BRM) repository in the avm directory MUST have the following directories and files:

  • /tests - (for unit tests and additional E2E/integration if required - e.g. Pester etc.)
    • /e2e - (all examples must deploy successfully - these will be used to automatically generate the examples in the README.md for the module)
  • /src - (for scripts and other files - e.g., scripts used by the template)
    • exampleFile.ps1
  • /modules - (for sub-modules only if used and NOT children of the primary resource - e.g. RBAC role assignments)
    • exampleTemplate.bicep
  • /main.bicep (AVM Module main .bicep file and entry point/orchestration module)
  • /main.json (auto generated and what is published to the MCR via BRM)
  • /version.json (BRM requirement)
  • /README.md (auto generated AVM Module documentation)
  • /CHANGELOG.md (manually maintained changelog file with one entry per published version)

Directory and File Structure Example

/ Root of Azure/bicep-registry-modules
├───avm
│   ├───ptn
│   │   └───apptiervmss
│   │       │   main.bicep
│   │       │   main.json
│   │       │   README.md
│   │       │   CHANGELOG.md
│   │       │   version.json
│   │       ├───src (optional)
│   │       │   ├───Get-Cake.ps1
│   │       │   └───Find-Waldo.ps1
│   │       ├───modules (optional)
│   │       │   ├───helper.bicep
│   │       │   └───role-assignment.bicep
│   │       └───tests
│   │           ├───unit (optional)
│   │           └───e2e
│   │               ├───defaults
│   │               ├───waf-aligned
│   │               └───max
│   │
│   └───res
│       └───compute
│           └───virtual-machine
│               │   main.bicep
│               │   main.json
│               │   README.md
│               │   CHANGELOG.md
│               │   version.json
│               ├───src (optional)
│               │   ├───Set-Bug.ps1
│               │   └───Invoke-Promotion.ps1
│               ├───modules (optional)
│               │   ├───helper.bicep
│               │   └───role-assignment.bicep
│               └───tests
│                   ├───unit (optional)
│                   └───e2e
│                       ├───defaults
│                       ├───waf-aligned
│                       └───max
├───other repo dirs...
└───other repo files...



See origin...

ID: BCPNFR24 - Category: Naming/Composition - Deterministic Deployment Names

When a module references child, utility, or other modules, the deployment name MUST be deterministic. This means the deployment name must produce the same value for the same set of inputs across repeated deployments.

Why deterministic?

Azure Resource Manager has an 800-deployment limit per scope (resource group, subscription, management group, tenant). Non-deterministic names (e.g., those incorporating timestamps or utcNow()) create a new deployment object on every run, which can lead to this limit being reached over time.

While an automatic cleanup process exists for resource group and subscription scopes, it can take some time to take effect. Due to eventual consistency in the backend, the deployment count may not reflect the cleanup immediately, which can lead to failed deployments even when the actual number of deployments is below the 800 limit. Additionally, automatic cleanup does not apply to management group or tenant scopes.

We are actively working with the product team to enhance the cleanup process. In the meantime, deterministic deployment names provide a reliable way to keep deployment counts stable by overwriting previous deployment objects rather than creating new ones.

Deterministic deployment names cause Azure to overwrite the previous deployment object, keeping the deployment count stable regardless of how many times the module is deployed.

Requirement

Module owners MUST construct deployment names for referenced modules using uniqueString() seeded with the parent resource’s ID (<parentResource>.id) and location, rather than deployment().name, subscription().id, resourceGroup().id, utcNow(), or other non-deterministic or scope-level values.

The deployment name MUST follow the pattern:

'${uniqueString(<parentResource>.id, location)}-<ChildModuleDescriptor>-${index}'

Where:

SegmentDescription
uniqueString(<parentResource>.id, location)A deterministic hash derived from the parent resource’s resource ID and deployment location. This is both unique per resource instance and stable across deployments.
<ChildModuleDescriptor>A short, human-readable label identifying the child module being deployed (e.g., DB, Subnet, FederatedIdentityCred).
${index}The loop index variable, included when deploying in a loop. Omit for single (non-looped) deployments.
location parameter

If location is not available, for example when deploying a global resource that does not have a location property, it is acceptable to omit it. However, the <parentResource>.id MUST always be included as the primary seed for uniqueString.

Why parent resource ID?

Using the parent resource’s ID as the uniqueString seed provides two critical properties:

  1. Deterministic — the same parent resource always produces the same hash, so repeated deployments overwrite rather than accumulate.
  2. Collision-free — different parent resource instances produce different hashes, so deploying multiple instances of the same module type within the same scope does not cause naming collisions.
Why not subscription().id and resourceGroup().id separately?

The parent resource’s ID (e.g., /subscriptions/.../resourceGroups/.../providers/.../resourceName) already contains the subscription ID and resource group ID as segments. Using <parentResource>.id as a single input to uniqueString captures all of this context in one value, keeping the code concise and readable rather than passing multiple scope-level values separately.

Supporting multiple deployments of the same module at the same scope

A common scenario is deploying the same module type more than once within the same scope — for example, two different SQL servers each with their own set of databases, or two user-assigned identities each with their own federated credentials. Because the parent resource ID is unique per resource instance, the resulting deployment names will differ even when the child module type and index are identical. This ensures that parallel deployments of the same module at the same scope do not collide.

Other approaches fail on one or both of these properties:

ApproachDeterministic?Collision-free?Issue
deployment().nameChanges every deployment; hits 800-limit
utcNow() / timestampsChanges every deployment; hits 800-limit
subscription().id + resourceGroup().idSame hash for all resources in the same RG; collisions when deploying multiple instances
<parentResource>.id, locationRecommended — stable and unique per instance

Examples

Example 1: Single child module deployment

resource server 'Microsoft.Sql/servers@2023-05-01-preview' = { ... }

module server_database 'database/main.bicep' = {
  name: '${uniqueString(server.id, location)}-Sql-DB'
  params: {
    serverName: server.name
    (...)
  }
}

Example 2: Child module deployment in a loop

resource server 'Microsoft.Sql/servers@2023-05-01-preview' = { ... }

module server_databases 'database/main.bicep' = [for (database, index) in (databases ?? []): {
  name: '${uniqueString(server.id, location)}-Sql-DB-${index}'
  params: {
    serverName: server.name
    (...)
  }
}]



See origin...

ID: BCPNFR5 - Category: Composition - Role Assignments Role Definition Mapping Limits

As per BCPFR2, module owners MAY define common RBAC Role Definition names and IDs within a variable to allow consumers to define a RBAC Role Definition by their name rather than their ID.

Module owners SHOULD NOT map every RBAC Role Definition within this variable as it can cause the module to bloat in size and cause consumption issues later when stitched together with other modules due to the 4MB ARM Template size limit.

Therefore module owners SHOULD only map the most applicable and common RBAC Role Definition names for their module and SHOULD NOT exceed 15 RBAC Role Definitions in the variable.

Important

Remember if the RBAC Role Definition name is not included in the variable this does not mean it cannot be declared, used and assigned to an identity via an RBAC Role Assignment as part of a module, as any RBAC Role Definition can be specified via its ID without being in the variable.

Tip

Review the Bicep Contribution Guide’s ‘RBAC Role Definition Name Mapping’ section for a code sample to achieve this requirement.




See origin...

ID: BCPNFR6 - Category: Composition - Role Assignments Role Definition Mapping Compulsory Roles

Module owners MUST include the following roles in the variable for RBAC Role Definition names:

  • Owner - ID: 8e3af657-a8ff-443c-a75c-2fe8c4bcb635
  • Contributor - ID: b24988ac-6180-42a0-ab88-20f7382dd24c
  • Reader - ID: acdd72a7-3385-48ef-bd42-f606fba81ae7
  • User Access Administrator - ID: 18d7d88d-d35e-4fb5-a5c3-7773c20a72d9
  • Role Based Access Control Administrator (Preview) - ID: f58310d9-a9f6-439a-9e8d-f62e7b41a168
Tip

Review the Bicep Contribution Guide’s ‘RBAC Role Definition Name Mapping’ section for a code sample to achieve this requirement.




See origin...

ID: BCPNFR14 - Category: Composition - Versioning

To meet SNFR17 and depending on the changes you make, you may need to bump the version in the version.json file.

  {
    "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
    "version": "0.1"
  }
  

The version value is in the form of MAJOR.MINOR. The PATCH version will be incremented by the CI automatically when publishing the module to the Public Bicep Registry once the corresponding pull request is merged. Therefore, contributions that would only require an update of the patch version, can keep the version.json file intact.

For example, the version value should be:

  • 0.1 for new modules, so that they can be released as v0.1.0.
  • 1.0 once the module owner signs off the module is stable enough for it’s first Major release of v1.0.0.
  • 0.x for all feature updates between the first release v0.1.0 and the first Major release of v1.0.0.



Inputs / Outputs

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR14Data TypesSHOULDOwnerContributorBAU
2SNFR22Parameters/Variables for Resource IDsMUSTOwnerContributorBAU
3SNFR26Output - Parameters - DecoratorsMUSTOwnerContributorBAU
4PMNFR5Parameter/Variable NamingSHOULDOwnerContributorBAU
5BCPNFR1Complex data types - GeneralMUSTOwnerContributorBAU
6BCPNFR9Inputs - DecoratorsMUSTOwnerContributorBAU
7BCPNFR18User-defined types - SpecificationMUSTOwnerContributorBAU
8BCPNFR19User-defined types - NamingMUSTOwnerContributorBAU
9BCPNFR20User-defined types - ExportMUSTOwnerContributorBAU
10BCPNFR21User-defined types - DecoratorsMUSTOwnerContributorBAU
11BCPNFR7Parameter Requirement TypesMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.




See origin...

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}, SHOULD contain ResourceId/resource_id in its parameter/variable name when that parameter/variable is part of a user-defined type. This assists 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 a user-defined type: in Bicep its parameter name should be workspaceResourceId and the variable name in Terraform should be workspace_resource_id.

In that user-defined context, workspaceId is not descriptive enough and is ambiguous as to which ID is required to be input.

Special considerations for Bicep

If the property is nested in a parameter and you opt for a resource-derived type (that is, a schema defined by the resource provider), this requirement does not apply. We do however recommend to use a user-defined type whenever these cases occur to increase the module’s usability.

Example for the property subnetArmId of the Cognitive Service’s property networkInjections:

If using a user-defined type, you may define a type for the networkInjections parameter like

param networkInjections networkInjectionType?

@export()
type networkInjectionType = {
  subnetResourceId: string

  // (...)
}

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: [{
      subnetArmId: networkInjections.?subnetResourceId
      // (...)
    }]
  }
}

or a resource-derived type like

param networkInjections resourceInput<'Microsoft.CognitiveServices/accounts@2025-06-01'>.properties.networkInjections

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: networkInjections
  }
}



See origin...

ID: SNFR26 - Output-Parameters - Decorators

Output parameters MUST implement:

Output parameters
@description('The resourceId of your resource.')
output sampleResourceId string = sampleResource.id

@description('The key of your resource.')
@secure()
output sampleResourceKey string = sampleResource.key
# Resource output
output "foo" {
  description = "MyResource foo attribute"
  value = azurerm_resource_myresource.foo
}

# Output of a sensitive attribute
output "bar" {
  description = "MyResource bar attribute"
  value     = azurerm_resource_myresource.bar
  sensitive = true
}



See origin...

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

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




See origin...

ID: BCPNFR1 - Category: Inputs - Complex data types - General

To simplify the consumption experience for module consumers when interacting with complex data types input parameters, mainly objects and arrays, the Bicep features of Resource-Derived Types or User-Defined Types MUST be used and declared.

Tip

User-Defined Types are GA in Bicep as of version v0.21.1, Resource-Derived Types are GA as of version v0.34.1, please ensure you have this version(s) installed as a minimum.

Resource-Derived Types and User-Defined Types allow intellisense support in supported IDEs (e.g. Visual Studio Code) for complex input parameters using objects and array of objects.

v0.x Exemption

While we allow the release of major versions, starting with v1.0.0, retrofitting Resource-Derived Types and User-Defined Types for all modules will take a considerable amount of time.

Therefore, the addition of these features is currently NOT mandated/enforced. However, all modules MUST implement Resource-Derived Types and User-Defined Types prior to the release of their v1.0.0 version.




See origin...

ID: BCPNFR9 - Inputs - Decorators

Similar to BCPNFR21, input parameters MUST implement decorators such as description & secure (if sensitive).

Further, input parameters SHOULD implement decorators like allowed, minValue, maxValue, minLength & maxLength (and others if available) as they have a big positive impact on the module’s usability.

@description('Optional. The threshold of your resource.')
@minValue(1)
@maxValue(10)
param threshold: int?
@description('Required. The SKU of your resource.')
@allowed([
'Basic'
'Premium'
'Standard'
])
param sku string



See origin...

ID: BCPNFR18 - User-defined types - Specification

User-defined types (UDTs) MUST always be singular and non-nullable. The configuration of either should instead be done directly at the parameter or output that uses the type.

For example, instead of

param subnets subnetsType
type subnetsType = { ... }[]?

the type should be defined like

param subnets subnetType[]?
type subnetType = { ... }

The primary reason for this requirement is clarity. If not defined directly at the parameter or output, a user would always be required to check the type to understand how e.g., a parameter is expected.




See origin...

ID: BCPNFR19 - User-defined types - Naming

User-defined types (UDTs) MUST always end with the suffix (...)Type to make them obvious to users. In addition it is recommended to extend the suffix to (...)OutputType if a UDT is exclusively used for outputs.

type subnet = { ... } // Wrong
type subnetType = { ... } // Correct
type subnetOutputType = { ... } // Correct, if used only for outputs

Since User-defined types (UDTs) MUST always be singular as per BCPNFR18, their naming should reflect this and also be singular.

type subnetsType = { ... } // Wrong
type subnetType = { ... } // Correct



See origin...

ID: BCPNFR20 - User-defined types - Export

User-defined types (UDTs) SHOULD always be exported via the @export() annotation in every template they’re implemented in.

@export()
type subnetType = { ... }

Doing so has the benefit that other (e.g., parent) modules can import them and as such reduce code duplication. Also, if the module itself is published, users of the Public Bicep Registry can import the types independently of the module itself. One example where this can be useful is a pattern module that may re-use the same interface when referencing a module from the registry.




See origin...

ID: BCPNFR21 - User-defined types - Decorators

Similar to BCPNFR9, User-defined types (UDTs) MUST implement decorators such as description & secure (if sensitive). This is true for every property of the UDT, as well as the UDT itself.

Further, User-defined types SHOULD implement decorators like allowed, minValue, maxValue, minLength & maxLength (and others if available) as they have a big positive impact on the module’s usability.

@description('My type''s description.')
type myType = {
  @description('Optional. The threshold of your resource.')
  @minValue(1)
  @maxValue(10)
  threshold: int?

  @description('Required. The SKU of your resource.')
  sku: ('Basic' | 'Premium' | 'Standard')
}



See origin...

ID: BCPNFR7 - Category: Inputs - Parameter Requirement Types

Modules will have lots of parameters that will differ in their requirement type (required, optional, etc.). To help consumers understand what each parameter’s requirement type is, module owners MUST add the requirement type to the beginning of each parameter’s description. Below are the requirement types with a definition and example for the description decorator:

Parameter Requirement TypeDefinitionExample Description Decorator
RequiredThe parameter value must be provided. The parameter does not have a default value and hence the module expects and requires an input.@description('Required. <PARAMETER DESCRIPTION HERE...>')
ConditionalThe parameter value can be optional or required based on a condition, mostly based on the value provided to other parameters. Should contain a sentence starting with ‘Required if (…).’ to explain the condition.@description('Conditional. <PARAMETER DESCRIPTION HERE...>')
OptionalThe parameter value is not mandatory. The module provides a default value for the parameter.@description('Optional. <PARAMETER DESCRIPTION HERE...>')
GeneratedThe parameter value is generated within the module and should not be specified as input in most cases. A common example of this is the utcNow() function that is only supported as the input for a parameter value, and not inside a variable.@description('Generated. <PARAMETER DESCRIPTION HERE...>')



Testing

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR1Prescribed TestsMUSTOwnerContributorBAU
2SNFR2E2E TestingMUSTOwnerContributorBAU
3SNFR3AVM Compliance TestsMUSTOwnerContributorInitial
4SNFR4Unit TestsSHOULDOwnerContributorBAU
5SNFR5Upgrade TestsSHOULDOwnerContributorBAU
6SNFR6Static Analysis/Linting TestsMUSTOwnerContributorBAU
7SNFR7Idempotency TestsMUSTOwnerContributorBAU
8SNFR24Testing Child, Extension & Interface ResourcesMUSTOwnerContributorBAU
9BCPNFR10Test Bicep File NamingMUSTOwnerContributorBAU
10BCPNFR11Test ToolingMUSTOwnerContributorBAU
11BCPNFR12Deployment Test NamingMUSTOwnerContributorBAU
12BCPNFR13Test file metadataMUSTOwnerContributorBAU
13BCPNFR16Post-deployment testsMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: SNFR1 - Category: Testing - Prescribed Tests

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




See origin...

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.

Tip

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

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 & Bicep Log Analytics Workspace examples using simple/native declarations for use in E2E tests

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
  }
}
Skipping Deployments (SHOULD NOT)

Deployment tests are an important part of a module’s validation and a staple of AVM’s CI environment. However, there are situations where certain e2e-test-deployments cannot be performed against AVM’s test environment (e.g., if a special configuration/registration (such as certain AI models) is required). For these cases, the CI offers the possibility to ‘skip’ specific test cases by placing a file named .e2eignore in their test folder.

Note

A skipped test case is still added to the ‘Usage Examples’ section of the module’s readme and should be manually validated in regular intervals.

Details for use in E2E tests

You MUST add a note to the tests metadata description, which explains the excemption.

If you require that a test is skipped and add an “.e2eignore” file (e.g. \<module\>/tests/e2e/\<testname\>/.e2eignore) to a pull request, a member of the AVM Core Technical Bicep Team must approve set pull request. The content of the file is logged the module’s workflow runs and transparently communicates why the test case is skipped during the deployment validation stage. It iss hence important to specify the reason for skipping the deployment in this file.

Sample filecontent:

The test is skipped, as only one instance of this service can be deployed to a subscription.
Note

For resource modules, the ‘defaults’ and ‘waf-aligned’ tests can’t be skipped.

The deployment of a test can be skipped by adding a .e2eignore file into a test folder (e.g. /examples/<testname>).




See origin...

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.

Important

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.




See origin...

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.




See origin...

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.




See origin...

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.




See origin...

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.




See origin...

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

Module owners MUST test that child and extension resources and those Bicep or Terreform interface resources that are supported by their modules, are validated 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.




See origin...

ID: BCPNFR10 - Category: Testing - Test Bicep File Naming

Module owners MUST name their test .bicep files in the /tests/e2e/<defaults/waf-aligned/max/etc.> directories: main.test.bicep as the test framework (CI) relies upon this name.




See origin...

ID: BCPNFR11 - Category: Testing - Test Tooling

Module owners MUST use the below tooling for unit/linting/static/security analysis tests. These are also used in the AVM Compliance Tests.

  • PSRule for Azure
  • Pester
    • Some tests are provided as part of the AVM Compliance Tests, but you are free to also use Pester for your own tests.



See origin...

ID: BCPNFR12 - Category: Testing - Deployment Test Naming

Module owners MUST invoke the module in their test using the syntax:

module testDeployment '../../../main.bicep' =

Example 1: Working example with a single deployment

module testDeployment '../../../main.bicep' = {
  scope: resourceGroup
  name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
  params: {
    (...)
  }
}

Example 2: Working example using a deployment loop

@batchSize(1)
module testDeployment '../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
  scope: resourceGroup
  name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
  params: {
    (...)
  }
}]

The syntax is used by the ReadMe-generating utility to identify, pull & format usage examples.




See origin...

ID: BCPNFR13 - Category: Testing - Test file metadata

By default, the ReadMe-generating utility will create usage examples headers based on each e2e folder’s name.
Module owners MAY provide a custom name & description by specifying the metadata blocks name & description in their main.test.bicep test files.

For example:

metadata name = 'Using Customer-Managed-Keys with System-Assigned identity'
metadata description = 'This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.'

would lead to a header in the module’s readme.md file along the lines of

### Example 1: _Using Customer-Managed-Keys with System-Assigned identity_

This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.



See origin...

ID: BCPNFR16 - Category: Testing - Post-deployment tests

For each test case in the e2e folder, you can optionally add post-deployment Pester tests that are executed once the corresponding deployment completed and before the removal logic kicks in.

To leverage the feature you MUST:

  • Use Pester as a test framework in each test file

  • Name the file with the suffix "*.tests.ps1"

  • Place each test file the e2e test’s folder or any subfolder (e.g., e2e/max/myTest.tests.ps1 or e2e/max/tests/myTest.tests.ps1)

  • Implement an input parameter TestInputData in the following way:

    param (
        [Parameter(Mandatory = $false)]
        [hashtable] $TestInputData = @{}
    )

    Through this parameter you can make use of every output the main.test.bicep file returns, as well as the path to the test template file in case you want to extract data from it directly.

    For example, with an output such as output resourceId string = testDeployment[1].outputs.resourceId defined in the main.test.bicep file, the $TestInputData would look like:

    $TestInputData = @{
      DeploymentOutputs    = @{
        resourceId = @{
          Type  = "String"
          Value = "/subscriptions/***/resourceGroups/dep-***-keyvault.vaults-kvvpe-rg/providers/Microsoft.KeyVault/vaults/***kvvpe001"
        }
      }
      ModuleTestFolderPath = "/home/runner/work/bicep-registry-modules/bicep-registry-modules/avm/res/key-vault/vault/tests/e2e/private-endpoint"
    }

    A full test file may look like:

    ➕ Pester post-deployment test file example
    param (
        [Parameter(Mandatory = $false)]
        [hashtable] $TestInputData = @{}
    )
    
    Describe 'Validate private endpoint deployment' {
    
        Context 'Validate sucessful deployment' {
    
            It "Private endpoints should be deployed in resource group" {
    
                $keyVaultResourceId = $TestInputData.DeploymentOutputs.resourceId.Value
                $testResourceGroup = ($keyVaultResourceId -split '\/')[4]
                $deployedPrivateEndpoints = Get-AzPrivateEndpoint -ResourceGroupName $testResourceGroup
                $deployedPrivateEndpoints.Count | Should -BeGreaterThan 0
            }
        }
    }



Documentation

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR15Automatic Documentation GenerationMUSTOwnerContributorBAU
2SNFR16Examples/E2EMUSTOwnerContributorBAU
3BCPNFR2Module Documentation GenerationMUSTOwnerContributorBAU
4BCPNFR3Usage Example formatsMUSTOwnerContributorBAU
5BCPNFR4Parameter Input ExamplesMAYOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.




See origin...

ID: SNFR16 - Category: Documentation - Examples/E2E

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




See origin...

ID: BCPNFR2 - Category: Documentation - Module Documentation Generation

Note

This script/tool is currently being developed by the AVM team and will be made available very soon.

Bicep modules documentation MUST be automatically generated via the provided script/tooling from the AVM team, providing the following headings:

  • Title
  • Description
  • Navigation
  • Resource Types
  • Usage Examples
  • Parameters
  • Outputs
  • Cross-referenced modules



See origin...

ID: BCPNFR3 - Category: Documentation - Usage Example formats

Usage examples for Bicep modules MUST be provided in the following formats:

  • Bicep file (orchestration module style) - .bicep

    module <resourceName> 'br/public:avm/[res|ptn|utl]/<publishedModuleName>:>version<' = {
      name: '${uniqueString(deployment().name, location)}-test-<uniqueIdentifier>'
      params: { (...) }
    }
  • JSON / ARM Template Parameter Files - .json

    {
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
      "contentVersion": "1.0.0.0",
      "parameters": { (...) }
    }
Note

The above formats are currently automatically taken & generated from the tests/e2e tests. It is enough to run the Set-ModuleReadMe or Set-AVMModule functions (from the utilities folder) to update the usage examples in the readme(s).

Note

Bicep Parameter Files (.bicepparam) are being reviewed and considered by the AVM team for the usability and features at this time and will likely be added in the future.




See origin...

ID: BCPNFR4 - Category: Documentation - Parameter Input Examples

Bicep modules MAY provide parameter input examples for parameters using the metadata.example property via the @metadata() decorator.

Example:

@metadata({
  example: 'uksouth'
})
@description('Optional. Location for all resources.')
param location string = resourceGroup().location

@metadata({
  example: '''
  {
    keyName: 'myKey'
    keyVaultResourceId: '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/my-rg/providers/Microsoft.KeyVault/vaults/myvault'
    keyVersion: '6d143c1a0a6a453daffec4001e357de0'
    userAssignedIdentityResourceId '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentity'
  }
  '''
})
@description('Optional. The customer managed key definition.')
param customerManagedKey customerManagedKeyType

It is planned that these examples are automatically added to the module readme’s parameter descriptions when running either the Set-ModuleReadMe or Set-AVMModule scripts (available in the utilities folder).




Release / Publishing

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR17Semantic VersioningMUSTOwnerContributorBAU
2SNFR18Breaking ChangesSHOULDOwnerContributorBAU
3SNFR19Registries TargetedMUSTOwnerContributorBAU
4SNFR21Cross Language CollaborationSHOULDOwnerContributorBAU
5BCPNFR22Bicep Module ChangelogMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: SNFR17 - Category: Release - Semantic Versioning

Important

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

Module versioning before first Major version release 1.0.0

  • Initially modules MUST be released as version 0.1.0 and incremented via Minor and Patch versions only until the AVM Core Team are confident the AVM specifications are mature enough and appropriate CI test coverage is in place, plus the module owner is happy the module has been “road tested” and is now stable enough for its first Major release of version 1.0.0.

    Note

    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. 👍

  • Until first Major version 1.0.0 is released, given a version number X.Y.Z:

    • X Major version MUST NOT be bumped.
    • Y Minor version MUST be bumped when introducing breaking changes (which would normally bump Major after 1.0.0 release) or feature updates (same as it will be after 1.0.0 release).
    • Z Patch version MUST be bumped when introducing non-breaking, backward compatible bug fixes (same as it will be after 1.0.0 release).



See origin...

ID: SNFR18 - Category: Release - Breaking Changes

A module SHOULD avoid breaking changes, e.g., deprecating inputs vs. removing. If you need to implement changes that cause a breaking change, the major version should be increased.

Info

Modules that have not been released as 1.0.0 may introduce breaking changes, as explained in the previous ID SNFR17. That means that you have to introduce non-breaking and breaking changes with a minor version jump, as long as the module has not reached version 1.0.0.

There are, however, scenarios where you want to include breaking changes into a commit and not create a new major version. If you want to introduce breaking changes as part of a minor update, you can do so. In this case, it is essential to keep the change backward compatible, so that the existing code will continue to work. At a later point, another update can increase the major version and remove the code introduced for the backward compatibility.

Tip

See the language specific examples to find out how you can deal with deprecations in AVM modules.




See origin...

ID: SNFR19 - Category: Publishing - Registries Targeted

Modules MUST be published to their respective language public registries.

Tip

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




See origin...

ID: SNFR21 - Category: Publishing - Cross Language Collaboration

When the module owners of the same Resource, Pattern or Utility 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.




See origin...

ID: BCPNFR22 - Category: Publishing - Changelog

When a module to be published (i.e., that has a version.json file) is changed, an entry MUST be created in the CHANGELOG.md file in the module folder. A link to the latest version of the changelog file has to be included at the top of the file, just below the # Changelog line. It is surrounded by empty lines.

# Changelog

The latest version of the changelog can be found [here](https://github.com/Azure/bicep-registry-modules/blob/main/avm/<ptn|res|utl>/<namespace/modulename[/submodulePath]>/CHANGELOG.md).

For each new version, an entry MUST be created above all existing versions in the CHANGELOG.md file of the module.

## <version>

### Changes

- This changed
- And this also

### Breaking Changes

- None

Each version’s entry:

  • MUST contain two sections: Changes and Breaking Changes. At least one of them must have a meaningful entry and sections must not be left empty. A - None may be added as content for a section.
  • MUST exist only once.
  • All versions appear in descending order, which puts the most recent changes at the top.

What SHOULD be listed in the (Breaking) Changes section:

  • Relevant changes for the module
  • Changes in tests do not need to be added
Note

The versioning is following the SNFR17 - Semantic Versioning spec.

Example content of the CHANGELOG.md

A CHANGELOG.md file in the module’s root folder MUST start with the # Changelog header, followed by an empty line and a link to the latest published version of the changelog file, followed by another empty line. A section for each published version follows. Newer versions are placed above older versions.

# Changelog

The latest version of the changelog can be found [here](https://github.com/Azure/bicep-registry-modules/blob/main/avm/res/aad/domain-service/CHANGELOG.md).

## 0.2.1

### Changes

- Updated the referenced AVM common types

### Breaking Changes

- None

## 0.2.0

### Changes

- Implemented the minCPU parameter
- Updated the referenced VirtualNetwork module
- Updated the referenced AVM common types

### Breaking Changes

- The minCPU parameter is mandatory

## 0.1.0

### Changes

- Initial Release

### Breaking Changes

- None

Each bullet point should start with a capital letter.

Manual Editing

It is possible to modify the changelog content any time, e.g., to add missing versions, which will not create a new release of the module itself. Please note the following requirements in all cases:

  • All versions in the file, need to be valid and available as published version
  • Every version needs the two sections ## Changes and ## Breaking Changes with content
Note

Azure Verified Modules are artifacts in the Microsoft Container Registry (MCR). Every version of a module exists as a tag in the Container Registry and can be listed as tags for each module https://mcr.microsoft.com/v2/bicep/avm/(res|ptn|utl)/<namespace/modulename>/tags/list




Code Style

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1BCPNFR8Code Styling - lower camelCasingSHOULDOwnerContributorBAU
2BCPNFR17Code Styling - Type castingSHOULDOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: BCPNFR8 - Category: Composition - Code Styling - lower camelCasing

Module owners SHOULD use lower camelCasing for naming the following:

  • Parameters
  • Variables
  • Outputs
  • User Defined Types
  • Resources (symbolic names)
  • Modules (symbolic names)

For example: camelCasingExample (lowercase first word (entirely), with capital of first letter of all other words and rest of word in lowercase)




See origin...

ID: BCPNFR17 - Category: Composition - Code Styling - Type casting

To improve the usability of primitive module properties declared as strings, you SHOULD declare them using a type which better represents them, and apply any required casting in the module on behalf of the user.

For reference, please refer to the following examples:

Boolean as String

Boolean as String
@allowed([
  'false'
  'true'
])
param myParameterValue string = 'false'

resource myResource '(...)' = {
  (...)
  properties: {
    myParameter: myParameterValue
  }
}
param myParameterValue string = false

resource myResource '(...)' = {
  (...)
  properties: {
    myParameter: string(myParameterValue)
  }
}

Integer Array as String Array

Integer Array as String Array
@allowed([
  '1'
  '2'
  '3'
])
param zones array

resource myResource '(...)' = {
  (...)
  properties: {
    zones: zones
  }
}
@allowed([
  1
  2
  3
])
param zones int[]

resource myResource '(...)' = {
  (...)
  properties: {
    zones: map(zones, zone => string(zone))
  }
}



Bicep Resource Module Specifications

Contribution / Support

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR8Module Owner(s) GitHubMUSTOwnerInitial
2SNFR20GitHub Teams OnlyMUSTOwnerInitial
3SNFR9AVM & PG Teams GitHub Repo PermissionsMUSTOwnerInitial
4SNFR10MIT LicensingMUSTOwnerInitial
5SNFR11Issues Response TimesMUSTOwnerContributorBAU
6SNFR12Versions SupportedMUSTOwnerBAU
7SNFR23GitHub Repo LabelsMUSTOwnerBAU
8PMNFR4Missing Resource Module(s)MUSTOwnerContributorBAU
9BCPNFR15AVM Module Issue template fileMUSTOwnerBAU
➕ See Specifications for this category
See origin...

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.

Note

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.




See origin...

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 a GitHub team assigned for module owners. This team MUST be created in the Azure organization in GitHub.

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

Info

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.

Bicep

Important

As part of the module proposal process, the name of the GitHub team for each approved module is already defined in the respective Module Indexes (or CSV file). This team MUST be created (and used) for each module.

Module owners don’t need to construct the name of the GitHub team for their module themselves, instead they need use the name prescribed in the related CSV file, at the time of approval.

For a direct link, see the list of related index pages:

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!

Naming Convention

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

  • <hyphenated module name>-module-owners-bicep - to grant permissions for module owners on Bicep modules

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 == the role the GitHub Team is assigned to
  • <bicep == the language the module is written in

Examples:

  • avm-res-compute-virtualmachine-module-owners-bicep
Note

The naming convention for Bicep modules is slightly different than the naming convention for their respective GitHub teams.

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.

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

Grant permissions through team memberships

Note

In case of Bicep modules, permissions to the BRM repository (the repo of the Bicep Registry) are granted via assigning the -module-owners- 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 team to the respective parent, only the AVM core team can approve this parent-child relationship.

Module owners MUST create their -module-owners- team and as part of the provisioning process, they MUST request the addition of this team to its respective parent team (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.

Example - GitHub team 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.
Tip

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).

Note

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 Bicep reviewers, with the @Azure/ prefix, i.e., @Azure/avm-module-reviewers-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-module-reviewers-bicep

Terraform

Note

Access management for Terraform repositories now uses a single team, membership of which is managed using an internal entitlement management tool (Core Identity).

All module owners MUST request access to the avm-module-owners-terraform GitHub team via the Azure Verified Module Owners Terraform entitlement in Core Identity (Microsoft internal tool).




See origin...

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

Note

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

Terraform

Important

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.




See origin...

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

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




See origin...

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

A module owner MUST respond to logged issues as defined in the support statement. See Module Support for more information.




See origin...

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.




See origin...

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:

➕ AVM Standard GitHub 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 📣In the AVM repository: this module needs an owner to develop or maintain it. In the BRM repository: the module owner needs to review a PR.
FF0019
Needs: Module Contributor 📣This module needs secondary owner(s) or contributor(s) to develop or maintain it
C95474
Needs: Core Team 🧞‍♂️This item needs the AVM Core Team to review it
DB4503
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 Deprecated 🔴This is a request to deprecate a module
000000
Status: Module Orphaned 🟡The module has no owner and is therefore orphaned at this time
F4A460
Status: Ready For Repository Creation 📝This module is approved and the owner is ready for the repository to be created (Terraform)
136A41
Status: Repository Created 📄This module has had it's repository created and configured ready for owner contribution (Terraform)
27AB03
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: CI 🚀This issue is related to the AVM CI
74CFB0
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
Class: Utility Module 📦This is a utility module
CAD1DE
Class: Child Module 📦This is a child module
5E5186

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

➕ Set-AvmGitHubLabels.ps1

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
```shell
# 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
  



See origin...

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.

Exception

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




See origin...

ID: BCPNFR15 - Category: Contribution/Support - AVM Module Issue template file

Module owners MUST add an entry to the AVM Module Issue template file in the BRM repository (here). When the module is deprecated, this entry MUST be removed from the file.

Note

Through this approach, the AVM core team will allow raising a bug or feature request for a module, only after the module gets merged to the BRM repository.

The module name entry MUST be added to the dropdown list with id module-name-dropdown as an option, in alphabetical order.

Important

Module owners MUST ensure that the module name is added in alphabetical order, to simplify selecting the right module name when raising an AVM module issue.

Example - AVM Module Issue template module name entry for the Bicep resource module of Azure Virtual Network (avm/res/network/virtual-network):

- type: dropdown
  id: module-name-dropdown
  attributes:
    label: Module Name
    description: Which existing AVM module is this issue related to?
    options:
      ...
      - "avm/res/network/virtual-network"
      ...



Telemetry

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR3Deployment/Usage TelemetryMUSTOwnerInitial
2SFR4Telemetry Enablement FlexibilityMUSTOwnerInitial
3BCPFR4Telemetry EnablementMUSTOwnerContributorBAU
4BCPFR7Cross-Referencing ModulesMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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

Modules MUST provide the capability to collect deployment/usage telemetry as detailed in Telemetry further.

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.)

Telemetry Information Notice

Note

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

  • Bicep: Using the 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.

Module Class Applicability

This specification applies to all AVM module classes (resource, pattern, utility), however, in case of utility modules, telemetry collection MUST only be added when the utility module deploys any resources (e.g., a deployment script resource). If the utility module does not deploy any resources, telemetry collection MUST NOT be added.

Bicep

Important

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.

The value you need to use for your module is defined in the related module index. You can look it up on the index pages for Resource Modules, Pattern Modules and Utility Modules.

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

  • <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-
    • The prefixes: avm-ptn-
  • <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)
Note

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.

An example deployment name for the AVM Virtual Machine Resource Module would be: 46d3xbcp.res.compute-virtualmachine.1-2-3.eum3

An example deployment name for a shortened module name would be: 46d3xbcp.res.desktopvirtualization-appgroup.1-2-3.eum3

Tip

Terraform: Terraform uses a telemetry provider, the configuration of which is the same for every module and is included in the template repo.

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

Terraform

To enable telemetry data collection for Terraform modules, the modtm telemetry provider MUST be used. 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.

The modtm provider MUST be 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"
    }
  }
}



See origin...

ID: SFR4 - Category: Telemetry - Telemetry Enablement Flexibility

The telemetry collection MUST be on/enabled by default, however module consumers MUST be allowed to disable it by setting the below parameter/variable value to false:

  • Bicep: enableTelemetry
  • Terraform: enable_telemetry
Note

Whenever a module references AVM modules that implement the telemetry parameter (e.g., a pattern module that uses AVM resource modules), the telemetry parameter value MUST be passed through to these modules. This is necessary to ensure a consumer can reliably enable & disable the telemetry feature for all used modules.

This general specification can be modified for some use-cases, that are language specific:

Bicep

For cross-references in resource modules, the spec BCPFR7 also applies.

Terraform

Currently, no further requirements apply.




See origin...

ID: BCPFR4 - Category: Composition - Telemetry Enablement

To comply with specifications outlined in SFR3 & SFR4 you MUST incorporate the following code snippet into your modules. Place this code sample in the “top level” main.bicep file; it is not necessary to include it in any nested Bicep files (child modules), unless they are marked for direct publishing (Ref Child module publishing).

@description('Optional. Location for all resources.')
param location string = resourceGroup().location

@description('Optional. Enable/Disable usage telemetry for module.')
param enableTelemetry bool = true

#disable-next-line no-deployments-resources
resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) {
  name: take('46d3xbcp.res.compute-virtualmachine.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}', 64)
  properties: {
    mode: 'Incremental'
    template: {
      '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
      contentVersion: '1.0.0.0'
      resources: []
      outputs: {
        telemetry: {
          type: 'String'
          value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
        }
      }
    }
  }
}



See origin...

ID: BCPFR7 - Cross-Referencing published Modules

Resource modules, that reference other modules (child, utility, or other resource modules), MUST disable the telemetry on the referenced modules.

Note

This only applies to resource modules that reference other modules, such as:

  • other resource modules
  • utility modules
  • child-modules qualifying for publishing, i.e. having a version.json file in their directory and exposing the enableTelemetry input parameter

For pattern modules, SFR4 still applies.

A variable named enableReferencedModulesTelemetry is created in the main.bicep file of the module, that cross-references other published modules, and set to false. This variable is used to set the enableTelemetry parameter of cross-referenced modules.

var enableReferencedModulesTelemetry = false

// local referencing
module virtualNetwork_subnets 'subnet/main.bicep' = [
  for (subnet, index) in (subnets ?? []): {
    name: '${uniqueString(virtualNetwork.id, location)}-subnet-${index}'
    params: {
      (...)
      enableTelemetry: enableReferencedModulesTelemetry
    }
  }
]

// published module reference
module virtualNetwork_subnet 'br/public:avm/res/network/virtual-network/subnet:0.1.0' = {
  name: '${uniqueString(virtualNetwork.id, location)}-subnet-${index}'
    params: {
      (...)
      enableTelemetry: enableReferencedModulesTelemetry
    }
}



Naming / Composition

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR1Preview ServicesMUSTOwnerBAU
2SFR2WAF AlignedSHOULDOwnerBAU
3SFR5Availability ZonesMUSTOwnerInitial
4SFR6Data RedundancyMUSTOwnerInitial
5SNFR25Resource NamingMUSTOwnerInitial
6RMFR1Single Resource OnlyMUSTOwnerContributorBAU
7RMFR2No Resource Wrapper ModulesMUSTOwnerInitial
8RMFR3Resource GroupsMUSTOwnerContributorBAU
9RMFR4AVM Consistent Feature & Extension Resources Value AddMUSTOwnerContributorBAU
10RMFR5AVM Consistent Feature & Extension Resources Value Add Interfaces/SchemasMUSTOwnerContributorBAU
11RMFR8Dependency on child and other resourcesMUSTOwnerContributorBAU
12RMFR9End-of-life resource versionsSHOULDOwnerContributorBAU
13RMNFR1Module NamingMUSTOwnerInitial
14RMNFR3RP CollaborationSHOULDOwnerBAU
15BCPFR1Cross-Referencing ModulesMAYOwnerContributorBAU
16BCPFR2Role Assignments Role Definition MappingMUSTOwnerContributorBAU
17BCPFR6Cross-Referencing Child-ModulesMUSTOwnerContributorBAU
18BCPNFR19User-defined types - NamingMUSTOwnerContributorBAU
19BCPNFR23Module compositionMUSTOwnerContributorBAU
20BCPNFR24Deterministic Deployment NamesMUSTOwnerContributorBAU
21BCPNFR5Role Assignments Role Definition Mapping LimitsSHOULDOwnerContributorBAU
22BCPNFR6Role Assignments Role Definition Mapping Compulsory RolesMUSTOwnerContributorBAU
23BCPNFR14VersioningMUSTOwnerContributorBAU
24BCPRMNFR3Child resources structureMUSTOwnerContributorBAU
25BCPRMNFR4Multi-scope modulesMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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”



See origin...

ID: SFR2 - Category: Composition - WAF Aligned

Modules SHOULD set defaults in input parameters/variables to align to high priority/impact/severity 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.

Tip

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




See origin...

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.

Note

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




See origin...

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.

Note

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




See origin...

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>.

Tip

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.




See origin...

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.




See origin...

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

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




See origin...

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.




See origin...

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.

Note

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.

Tip

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




See origin...

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:




See origin...

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.




See origin...

ID: RMFR9 - Category: Composition - End-of-life resource versions

When a given version of an Azure resource used in a resource module reaches its end-of-life (EOL) and is no longer supported by Microsoft, the module owner SHOULD ensure that:

  1. The module is aligned with these changes and only includes supported versions of the resource. This is typically achieved through the allowed values in the parameter that specifies the resource SKU or type.
  2. The following notice is shown under the Notes section of the module’s readme.md. (If any related public announcement is available, it can also be linked to from the Notes section.):

    “Certain versions of this Azure resource reached their end of life. The latest version of this module only includes supported versions of the resource. All unsupported versions have been removed from the related parameters.”

  3. AND the related parameter’s description:

    “Certain versions of this Azure resource reached their end of life. The latest version of this module only includes supported versions of the resource. All unsupported versions have been removed from this parameter.”




See origin...

ID: RMNFR1 - Category: Naming - Module Naming

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

Important

As part of the module proposal process, the module’s approved name is captured both in the module proposal issue AND the related module index page (backed by the corresponding CSV file).

Therefore, module owners don’t need to construct the module’s name themselves, instead they need use the name prescribed in the module proposal issue or in the related CSV file, at the time of approval.

Note

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.

Bicep Resource Module Naming

  • Naming convention (module name for registry): avm/res/<hyphenated resource provider name>/<hyphenated ARM resource type>
  • 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.

Bicep Child Module Naming

  • Naming convention (module name for registry):avm/res/<hyphenated resource provider name>/<hyphenated ARM resource type>/ <hyphenated child resource type/<hyphenated grandchild resource type>/<etc.>

  • Example: avm/res/network/virtual-network/subnet or avm/res/storage/storage-account/blob-service/container

  • 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.Network = network.
    • <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.Network/virtualNetworks = virtual-network.
    • <hyphenated child resource type (to be repeated for grandchildren, etc.)> 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.Network/virtualNetworks/subnets = subnet or Microsoft.Storage/storageAccounts/blobServices/containers = blob-service/container.

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



See origin...

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.




See origin...

ID: BCPFR1 - Category: Composition - Cross-Referencing Modules

Module owners MAY cross-reference other modules to build either Resource or Pattern modules.

However, they MUST be referenced only by a public registry reference to a pinned version e.g. br/public:avm/[res|ptn|utl]/<publishedModuleName>:>version<. They MUST NOT use local parent path references to a module e.g. ../../xxx/yyy.bicep.

The only exception to this rule are child modules as documented in BCPFR6.

Modules MUST NOT contain references to non-AVM modules.




See origin...

ID: BCPFR2 - Category: Composition - Role Assignments Role Definition Mapping

Module owners MAY define common RBAC Role Definition names and IDs within a variable to allow consumers to define a RBAC Role Definition by their name rather than their ID, this should be self contained within the module themselves.

However, they MUST use only the official RBAC Role Definition name within the variable and nothing else.

To meet the requirements of BCPFR2, BCPNFR5 and BCPNFR6 you MUST use the below code sample in your AVM Modules to achieve this.

  @description('''Required. You can provide either the display name (note not all roles are supported, check module documentation) of the role definition, or its fully qualified ID in the following format: `/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11`.''')
  param roleDefinitionIdOrName string
  
  var builtInRbacRoleNames = {
    Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
    Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c'
    Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7'
    'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168'
    'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'
    //Other RBAC Role Definitions Names & IDs can be added here as needed for your module
  }
  
  var roleDefinitionIdMappedResult = (contains(builtInRbacRoleNames, roleDefinitionIdOrName) ? builtInRbacRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName)
  
  resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
    //Other properties removed for ease of reading
    properties: {
      roleDefinitionId: roleDefinitionIdMappedResult
      //Other properties removed for ease of reading
    }
  }
  



See origin...

ID: BCPFR6 - Cross-Referencing Child-Modules

Parent templates MUST reference all their direct child-templates to allow for an end-to-end deployment experience.
For example, the SQL server template must reference its child database module and encapsulate it in a loop to allow for the deployment of multiple databases.

@description('Optional. The databases to create in the server')
param databases databaseType[]?

resource server 'Microsoft.Sql/servers@(...)' = { (...) }

module server_databases 'database/main.bicep' = [for (database, index) in (databases ?? []): {
  name: '${uniqueString(server.id, location)}-Sql-DB-${index}'
  params: {
    serverName: server.name
    (...)
  }
}]



See origin...

ID: BCPNFR19 - User-defined types - Naming

User-defined types (UDTs) MUST always end with the suffix (...)Type to make them obvious to users. In addition it is recommended to extend the suffix to (...)OutputType if a UDT is exclusively used for outputs.

type subnet = { ... } // Wrong
type subnetType = { ... } // Correct
type subnetOutputType = { ... } // Correct, if used only for outputs

Since User-defined types (UDTs) MUST always be singular as per BCPNFR18, their naming should reflect this and also be singular.

type subnetsType = { ... } // Wrong
type subnetType = { ... } // Correct



See origin...

ID: BCPNFR23 - Category: Composition

Each Bicep AVM module that lives within the Azure/bicep-registry-modules (BRM) repository in the avm directory MUST have the following directories and files:

  • /tests - (for unit tests and additional E2E/integration if required - e.g. Pester etc.)
    • /e2e - (all examples must deploy successfully - these will be used to automatically generate the examples in the README.md for the module)
  • /src - (for scripts and other files - e.g., scripts used by the template)
    • exampleFile.ps1
  • /modules - (for sub-modules only if used and NOT children of the primary resource - e.g. RBAC role assignments)
    • exampleTemplate.bicep
  • /main.bicep (AVM Module main .bicep file and entry point/orchestration module)
  • /main.json (auto generated and what is published to the MCR via BRM)
  • /version.json (BRM requirement)
  • /README.md (auto generated AVM Module documentation)
  • /CHANGELOG.md (manually maintained changelog file with one entry per published version)

Directory and File Structure Example

/ Root of Azure/bicep-registry-modules
├───avm
│   ├───ptn
│   │   └───apptiervmss
│   │       │   main.bicep
│   │       │   main.json
│   │       │   README.md
│   │       │   CHANGELOG.md
│   │       │   version.json
│   │       ├───src (optional)
│   │       │   ├───Get-Cake.ps1
│   │       │   └───Find-Waldo.ps1
│   │       ├───modules (optional)
│   │       │   ├───helper.bicep
│   │       │   └───role-assignment.bicep
│   │       └───tests
│   │           ├───unit (optional)
│   │           └───e2e
│   │               ├───defaults
│   │               ├───waf-aligned
│   │               └───max
│   │
│   └───res
│       └───compute
│           └───virtual-machine
│               │   main.bicep
│               │   main.json
│               │   README.md
│               │   CHANGELOG.md
│               │   version.json
│               ├───src (optional)
│               │   ├───Set-Bug.ps1
│               │   └───Invoke-Promotion.ps1
│               ├───modules (optional)
│               │   ├───helper.bicep
│               │   └───role-assignment.bicep
│               └───tests
│                   ├───unit (optional)
│                   └───e2e
│                       ├───defaults
│                       ├───waf-aligned
│                       └───max
├───other repo dirs...
└───other repo files...



See origin...

ID: BCPNFR24 - Category: Naming/Composition - Deterministic Deployment Names

When a module references child, utility, or other modules, the deployment name MUST be deterministic. This means the deployment name must produce the same value for the same set of inputs across repeated deployments.

Why deterministic?

Azure Resource Manager has an 800-deployment limit per scope (resource group, subscription, management group, tenant). Non-deterministic names (e.g., those incorporating timestamps or utcNow()) create a new deployment object on every run, which can lead to this limit being reached over time.

While an automatic cleanup process exists for resource group and subscription scopes, it can take some time to take effect. Due to eventual consistency in the backend, the deployment count may not reflect the cleanup immediately, which can lead to failed deployments even when the actual number of deployments is below the 800 limit. Additionally, automatic cleanup does not apply to management group or tenant scopes.

We are actively working with the product team to enhance the cleanup process. In the meantime, deterministic deployment names provide a reliable way to keep deployment counts stable by overwriting previous deployment objects rather than creating new ones.

Deterministic deployment names cause Azure to overwrite the previous deployment object, keeping the deployment count stable regardless of how many times the module is deployed.

Requirement

Module owners MUST construct deployment names for referenced modules using uniqueString() seeded with the parent resource’s ID (<parentResource>.id) and location, rather than deployment().name, subscription().id, resourceGroup().id, utcNow(), or other non-deterministic or scope-level values.

The deployment name MUST follow the pattern:

'${uniqueString(<parentResource>.id, location)}-<ChildModuleDescriptor>-${index}'

Where:

SegmentDescription
uniqueString(<parentResource>.id, location)A deterministic hash derived from the parent resource’s resource ID and deployment location. This is both unique per resource instance and stable across deployments.
<ChildModuleDescriptor>A short, human-readable label identifying the child module being deployed (e.g., DB, Subnet, FederatedIdentityCred).
${index}The loop index variable, included when deploying in a loop. Omit for single (non-looped) deployments.
location parameter

If location is not available, for example when deploying a global resource that does not have a location property, it is acceptable to omit it. However, the <parentResource>.id MUST always be included as the primary seed for uniqueString.

Why parent resource ID?

Using the parent resource’s ID as the uniqueString seed provides two critical properties:

  1. Deterministic — the same parent resource always produces the same hash, so repeated deployments overwrite rather than accumulate.
  2. Collision-free — different parent resource instances produce different hashes, so deploying multiple instances of the same module type within the same scope does not cause naming collisions.
Why not subscription().id and resourceGroup().id separately?

The parent resource’s ID (e.g., /subscriptions/.../resourceGroups/.../providers/.../resourceName) already contains the subscription ID and resource group ID as segments. Using <parentResource>.id as a single input to uniqueString captures all of this context in one value, keeping the code concise and readable rather than passing multiple scope-level values separately.

Supporting multiple deployments of the same module at the same scope

A common scenario is deploying the same module type more than once within the same scope — for example, two different SQL servers each with their own set of databases, or two user-assigned identities each with their own federated credentials. Because the parent resource ID is unique per resource instance, the resulting deployment names will differ even when the child module type and index are identical. This ensures that parallel deployments of the same module at the same scope do not collide.

Other approaches fail on one or both of these properties:

ApproachDeterministic?Collision-free?Issue
deployment().nameChanges every deployment; hits 800-limit
utcNow() / timestampsChanges every deployment; hits 800-limit
subscription().id + resourceGroup().idSame hash for all resources in the same RG; collisions when deploying multiple instances
<parentResource>.id, locationRecommended — stable and unique per instance

Examples

Example 1: Single child module deployment

resource server 'Microsoft.Sql/servers@2023-05-01-preview' = { ... }

module server_database 'database/main.bicep' = {
  name: '${uniqueString(server.id, location)}-Sql-DB'
  params: {
    serverName: server.name
    (...)
  }
}

Example 2: Child module deployment in a loop

resource server 'Microsoft.Sql/servers@2023-05-01-preview' = { ... }

module server_databases 'database/main.bicep' = [for (database, index) in (databases ?? []): {
  name: '${uniqueString(server.id, location)}-Sql-DB-${index}'
  params: {
    serverName: server.name
    (...)
  }
}]



See origin...

ID: BCPNFR5 - Category: Composition - Role Assignments Role Definition Mapping Limits

As per BCPFR2, module owners MAY define common RBAC Role Definition names and IDs within a variable to allow consumers to define a RBAC Role Definition by their name rather than their ID.

Module owners SHOULD NOT map every RBAC Role Definition within this variable as it can cause the module to bloat in size and cause consumption issues later when stitched together with other modules due to the 4MB ARM Template size limit.

Therefore module owners SHOULD only map the most applicable and common RBAC Role Definition names for their module and SHOULD NOT exceed 15 RBAC Role Definitions in the variable.

Important

Remember if the RBAC Role Definition name is not included in the variable this does not mean it cannot be declared, used and assigned to an identity via an RBAC Role Assignment as part of a module, as any RBAC Role Definition can be specified via its ID without being in the variable.

Tip

Review the Bicep Contribution Guide’s ‘RBAC Role Definition Name Mapping’ section for a code sample to achieve this requirement.




See origin...

ID: BCPNFR6 - Category: Composition - Role Assignments Role Definition Mapping Compulsory Roles

Module owners MUST include the following roles in the variable for RBAC Role Definition names:

  • Owner - ID: 8e3af657-a8ff-443c-a75c-2fe8c4bcb635
  • Contributor - ID: b24988ac-6180-42a0-ab88-20f7382dd24c
  • Reader - ID: acdd72a7-3385-48ef-bd42-f606fba81ae7
  • User Access Administrator - ID: 18d7d88d-d35e-4fb5-a5c3-7773c20a72d9
  • Role Based Access Control Administrator (Preview) - ID: f58310d9-a9f6-439a-9e8d-f62e7b41a168
Tip

Review the Bicep Contribution Guide’s ‘RBAC Role Definition Name Mapping’ section for a code sample to achieve this requirement.




See origin...

ID: BCPNFR14 - Category: Composition - Versioning

To meet SNFR17 and depending on the changes you make, you may need to bump the version in the version.json file.

  {
    "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
    "version": "0.1"
  }
  

The version value is in the form of MAJOR.MINOR. The PATCH version will be incremented by the CI automatically when publishing the module to the Public Bicep Registry once the corresponding pull request is merged. Therefore, contributions that would only require an update of the patch version, can keep the version.json file intact.

For example, the version value should be:

  • 0.1 for new modules, so that they can be released as v0.1.0.
  • 1.0 once the module owner signs off the module is stable enough for it’s first Major release of v1.0.0.
  • 0.x for all feature updates between the first release v0.1.0 and the first Major release of v1.0.0.



See origin...

ID: BCPRMNFR3 - Implementing child resources

Child resource modules MUST be stored in a subfolder of their parent resource module and named after the child resource’s singular name (ref), so that the path to the child resource folder is consistent with the hierarchy of its resource type.
For example, Microsoft.Sql/servers may have dedicated child resources of type Microsoft.Sql/servers/databases. Hence, the SQL server database child module is stored in a database subfolder of the server parent folder.

sql
└─ server [module]
  └─ database [child-module/resource]

In this folder, we recommend to place the child resource-template alongside a ReadMe & compiled JSON (to be generated via the default Set-AVMModule utility) and optionally further nest additional folders for its child resources.

There are several reasons to structure a module in this way. For example:

  • It allows a separation of concerns where each module can focus on its own properties and logic, while delegating most of a child-resource’s logic to its separate child module
  • It’s consistent with the provider namespace structure and makes modules easier to understand not only because they’re more aligned with set structure, but also are aligned with one another
  • As each module is its own ‘deployment’, it reduces limitations around nested loops
  • It enables module owners to publish child-modules as separate modules to the public registry, allowing consumers to make use of them directly [Ref child module publishing guidelines for details].
Note

In full transparency: The drawbacks of these additional deployments is an extended deployment period & a contribution to the 800 deployments limit. However, for AVM resource modules it was agreed that the advantages listed above outweigh these limitations.




See origin...

ID: BCPRMNFR4 - Implementing multi-scope modules

Several resource types in Azure (e.g., role-assignments, budgets, etc.) may be deployed to more than one scope (e.g., subscription, management-group, etc.).
In AVM, such modules can be implemented in one of two ways:

  1. As pattern modules with one ‘orchestrating’ parent module using scoped sub-modules based on the input parameters provided

Note: Only the parent module is published. I.e., it is not possible to target e.g., the resource-group scoped sub-module directly.

  1. As resource modules where each scope is implemented as a child-module of a non-published parent.

Note: Each child module is published, but not the parent. I.e., it is possible to target e.g., the resource-group scoped sub-module directly.

Tip

It is highly recommended to publish multi-scoped modules as resource modules as the solution provides the best user experience.

Considerations when published as a pattern module

Example: avm/ptn/authorization/role-assignment

Note

The following instructions consider all deployment scopes. Your module may only deploy to a subset of the same and you should map the conventions to your case.

To successfully implement a multi-scoped module as a pattern modules you have to adhere to the following convention:

  • The parent module MUST be implemented in the highest scope the resource provider supports (e.g., management-group)
  • The parent module MUST have one sub-module for each scope that the resource provider supports (e.g., management-group, subscription & resource-group)
  • Each sub-module MUST be implemented for the scope it is intended
  • The parent module MUST invoke each sub-module in the scope it is written for, using input parameters needed to target set scope (e.g., a subscription-id to invoke a module for set scope)
  • The parent module MUST have test cases to validate each sub-module
  • The parent module is the one that is versioned, published and maintains a changelog

The full folder structure may look like

📄main.bicep                 [Orchestrating module]
📄main.json                  [ARM JSON file of the module]
📄version.json               [Version file of the module]
📄README.md                  [Readme of the module]
📄CHANGELOG.md               [The changelog of the module]
┣ 📂modules
┃ ┣ 📄management-group.bicep [Sub-module deploying to the mgmt-group scope (if applicable)]
┃ ┣ 📄subscription.bicep     [Sub-module deploying to the subscription scope (if applicable)]
┃ ┗ 📄resource-group.bicep   [Sub-module deploying to the resource-group scope (if applicable)]
┗ 📂tests/e2e
  ┣ 📂 mg.defaults
  ┃ ┗ 📄main.test.bicep      [deploys parent template]
  ┣ 📂 mg.waf-aligned
  ┃ ┗ 📄main.test.bicep      [deploys parent template]
  ┣ 📂 sub.defaults
  ┃ ┗ 📄main.test.bicep      [deploys parent template with `subscriptionId` param]
  ┣ 📂 sub.waf-aligned
  ┃ ┗ 📄main.test.bicep      [deploys parent template with `subscriptionId` param]
  ┣ 📂 rg.defaults
  ┃ ┗ 📄main.test.bicep      [deploys parent template with `subscriptionId` & `resourceGroupName` params]
  ┗ 📂 rg.waf-aligned
    ┗ 📄main.test.bicep      [deploys parent template with `subscriptionId` & `resourceGroupName` params]
Warning

Even if a consumer wants to deploy to one of the sub-scopes (e.g., subscription), the module must be deployed via its parent (e.g., management-group). This can be confusing for consumers at first and should be considered when implementing the solution.

Example: To use a role-assignment pattern module (which would be written for all scopes, with the parent targeting the management-group scope) to deploy role assignments to a resource group, a user would need to invoke New-AzManagementGroupDeployment and provide the parameters for both the subscription & resource-group to target. I.e., the user must have permissions to deploy to each scope.

Considerations when published as a resource module

Example: avm/res/authorization/role-assignment

Note

The following instructions consider all deployment scopes. Your module may only deploy to a subset of the same and you should map the conventions to your case.

To successfully implement a multi-scoped module as a resource modules you have to adhere to the following convention:

  • The parent folder MUST contain a

    • main.bicep file
    • main.json file
    • README.md file
    • tests/e2e folder
    • One folder per each scope the resource provider can deploy to (either mg-scope, sub-scope or rg-scope).
  • Each child-module folder MUST be implemented as a proper child module, with a

    • main.bicep
    • main.json
    • version.json
    • README.md
    • CHANGELOG.md

    file. Each child-module is maintained and versioned independently of the others.

  • The parent main.bicep MUST contain the following information

    metadata name = '<Module Name> (Multi-Scope)'
    metadata description = '''
    This module's child-modules deploy a <Placeholder> at a Management Group (mg-scope), Subscription (sub-scope) or   Resource Group (rg-scope) scope.
    
    > While this template is **not** published, you can find the actual published modules in the subfolders
    > - `mg-scope`
    > - `sub-scope`
    > - `rg-scope`
    '''
    targetScope = 'managementGroup'

    updated with your module’s specifics

  • The tests/e2e folder MUST contain one instance of the require test cases per each scope, and MAY contain any additional test you see fit. In each case, the scope MUST be a prefix for the folder name. Each test case MUST reference the corresponding child module directly.

The full folder structure may look like

📄main.bicep                [Skeleton module with disclaimer referring to the child-modules]
📄main.json                 [ARM JSON file of the module]
📄README.md                 [The baseline readme, surfacing the metadata of the main.bicep file]
┣ 📂mg-scope
┃ ┣📄main.bicep             [Module deploying to mg-scope]
┃ ┣📄main.json              [ARM JSON file of the module]
┃ ┣📄README.md              [Readme of the module]
┃ ┣📄version.json           [Version file of the module]
┃ ┗📄CHANGELOG.md           [The changelog of the module]
┣ 📂sub-scope
┃ ┣📄main.bicep             [Module deploying to sub-scope]
┃ ┣📄main.json              [ARM JSON file of the module]
┃ ┣📄README.md              [Readme of the module]
┃ ┣📄version.json           [Version file of the module]
┃ ┗📄CHANGELOG.md           [The changelog of the module]
┣ 📂rg-scope
┃ ┣📄main.bicep             [Module deploying to rg-scope]
┃ ┣📄main.json              [ARM JSON file of the module]
┃ ┣📄README.md              [Readme of the module]
┃ ┣📄version.json           [Version file of the module]
┃ ┗📄CHANGELOG.md           [The changelog of the module]
┗ 📂tests/e2e
  ┣ 📂mg-scope.defaults
  ┃ ┗📄main.test.bicep      [references the 'mg-scope' child module template: '../../../mg-scope/main.bicep']
  ┣ 📂mg-scope.waf-aligned
  ┃ ┗📄main.test.bicep      [references the 'mg-scope' child module template: '../../../mg-scope/main.bicep']
  ┣ 📂mg-scope.max
  ┃ ┗📄main.test.bicep      [references the 'mg-scope' child module template: '../../../mg-scope/main.bicep']
  ┣ 📂sub-scope.defaults
  ┃ ┗📄main.test.bicep      [references the 'sub-scope' child module template: '../../../sub-scope/main.bicep']
  ┣ 📂sub-scope.waf-aligned
  ┃ ┗📄main.test.bicep      [references the 'sub-scope' child module template: '../../../sub-scope/main.bicep']
  ┣ 📂rg-scope.defaults
  ┃ ┗📄main.test.bicep      [references the 'rg-scope' child module template: '../../../rg-scope/main.bicep']
  ┗ 📂rg-scope.waf-aligned
    ┗📄main.test.bicep      [references the 'rg-scope' child module template: '../../../rg-scope/main.bicep']
Important

Because each child-module is published on its own, you must ensure that each is registered in the MAR-file before the modules can be published. The MAR-file can only be accessed by Microsoft FTEs.

Please highlight the nature of your module in the issue when proposing it to AVM.




Inputs / Outputs

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR14Data TypesSHOULDOwnerContributorBAU
2SNFR22Parameters/Variables for Resource IDsMUSTOwnerContributorBAU
3SNFR26Output - Parameters - DecoratorsMUSTOwnerContributorBAU
4RMFR6Parameter/Variable NamingMUSTOwnerContributorBAU
5RMFR7Minimum Required OutputsMUSTOwnerContributorBAU
6RMNFR2Parameter/Variable NamingMUSTOwnerContributorBAU
7BCPNFR1Complex data types - GeneralMUSTOwnerContributorBAU
8BCPNFR9Inputs - DecoratorsMUSTOwnerContributorBAU
9BCPNFR18User-defined types - SpecificationMUSTOwnerContributorBAU
10BCPNFR19User-defined types - NamingMUSTOwnerContributorBAU
11BCPNFR20User-defined types - ExportMUSTOwnerContributorBAU
12BCPNFR21User-defined types - DecoratorsMUSTOwnerContributorBAU
13BCPNFR7Parameter Requirement TypesMUSTOwnerContributorBAU
14BCPRMNFR2User-defined types - AVM-Common-TypesMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.




See origin...

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}, SHOULD contain ResourceId/resource_id in its parameter/variable name when that parameter/variable is part of a user-defined type. This assists 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 a user-defined type: in Bicep its parameter name should be workspaceResourceId and the variable name in Terraform should be workspace_resource_id.

In that user-defined context, workspaceId is not descriptive enough and is ambiguous as to which ID is required to be input.

Special considerations for Bicep

If the property is nested in a parameter and you opt for a resource-derived type (that is, a schema defined by the resource provider), this requirement does not apply. We do however recommend to use a user-defined type whenever these cases occur to increase the module’s usability.

Example for the property subnetArmId of the Cognitive Service’s property networkInjections:

If using a user-defined type, you may define a type for the networkInjections parameter like

param networkInjections networkInjectionType?

@export()
type networkInjectionType = {
  subnetResourceId: string

  // (...)
}

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: [{
      subnetArmId: networkInjections.?subnetResourceId
      // (...)
    }]
  }
}

or a resource-derived type like

param networkInjections resourceInput<'Microsoft.CognitiveServices/accounts@2025-06-01'>.properties.networkInjections

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: networkInjections
  }
}



See origin...

ID: SNFR26 - Output-Parameters - Decorators

Output parameters MUST implement:

Output parameters
@description('The resourceId of your resource.')
output sampleResourceId string = sampleResource.id

@description('The key of your resource.')
@secure()
output sampleResourceKey string = sampleResource.key
# Resource output
output "foo" {
  description = "MyResource foo attribute"
  value = azurerm_resource_myresource.foo
}

# Output of a sensitive attribute
output "bar" {
  description = "MyResource bar attribute"
  value     = azurerm_resource_myresource.bar
  sensitive = true
}



See origin...

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.




See origin...

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
Tip

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




See origin...

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)



See origin...

ID: BCPNFR1 - Category: Inputs - Complex data types - General

To simplify the consumption experience for module consumers when interacting with complex data types input parameters, mainly objects and arrays, the Bicep features of Resource-Derived Types or User-Defined Types MUST be used and declared.

Tip

User-Defined Types are GA in Bicep as of version v0.21.1, Resource-Derived Types are GA as of version v0.34.1, please ensure you have this version(s) installed as a minimum.

Resource-Derived Types and User-Defined Types allow intellisense support in supported IDEs (e.g. Visual Studio Code) for complex input parameters using objects and array of objects.

v0.x Exemption

While we allow the release of major versions, starting with v1.0.0, retrofitting Resource-Derived Types and User-Defined Types for all modules will take a considerable amount of time.

Therefore, the addition of these features is currently NOT mandated/enforced. However, all modules MUST implement Resource-Derived Types and User-Defined Types prior to the release of their v1.0.0 version.




See origin...

ID: BCPNFR9 - Inputs - Decorators

Similar to BCPNFR21, input parameters MUST implement decorators such as description & secure (if sensitive).

Further, input parameters SHOULD implement decorators like allowed, minValue, maxValue, minLength & maxLength (and others if available) as they have a big positive impact on the module’s usability.

@description('Optional. The threshold of your resource.')
@minValue(1)
@maxValue(10)
param threshold: int?
@description('Required. The SKU of your resource.')
@allowed([
'Basic'
'Premium'
'Standard'
])
param sku string



See origin...

ID: BCPNFR18 - User-defined types - Specification

User-defined types (UDTs) MUST always be singular and non-nullable. The configuration of either should instead be done directly at the parameter or output that uses the type.

For example, instead of

param subnets subnetsType
type subnetsType = { ... }[]?

the type should be defined like

param subnets subnetType[]?
type subnetType = { ... }

The primary reason for this requirement is clarity. If not defined directly at the parameter or output, a user would always be required to check the type to understand how e.g., a parameter is expected.




See origin...

ID: BCPNFR19 - User-defined types - Naming

User-defined types (UDTs) MUST always end with the suffix (...)Type to make them obvious to users. In addition it is recommended to extend the suffix to (...)OutputType if a UDT is exclusively used for outputs.

type subnet = { ... } // Wrong
type subnetType = { ... } // Correct
type subnetOutputType = { ... } // Correct, if used only for outputs

Since User-defined types (UDTs) MUST always be singular as per BCPNFR18, their naming should reflect this and also be singular.

type subnetsType = { ... } // Wrong
type subnetType = { ... } // Correct



See origin...

ID: BCPNFR20 - User-defined types - Export

User-defined types (UDTs) SHOULD always be exported via the @export() annotation in every template they’re implemented in.

@export()
type subnetType = { ... }

Doing so has the benefit that other (e.g., parent) modules can import them and as such reduce code duplication. Also, if the module itself is published, users of the Public Bicep Registry can import the types independently of the module itself. One example where this can be useful is a pattern module that may re-use the same interface when referencing a module from the registry.




See origin...

ID: BCPNFR21 - User-defined types - Decorators

Similar to BCPNFR9, User-defined types (UDTs) MUST implement decorators such as description & secure (if sensitive). This is true for every property of the UDT, as well as the UDT itself.

Further, User-defined types SHOULD implement decorators like allowed, minValue, maxValue, minLength & maxLength (and others if available) as they have a big positive impact on the module’s usability.

@description('My type''s description.')
type myType = {
  @description('Optional. The threshold of your resource.')
  @minValue(1)
  @maxValue(10)
  threshold: int?

  @description('Required. The SKU of your resource.')
  sku: ('Basic' | 'Premium' | 'Standard')
}



See origin...

ID: BCPNFR7 - Category: Inputs - Parameter Requirement Types

Modules will have lots of parameters that will differ in their requirement type (required, optional, etc.). To help consumers understand what each parameter’s requirement type is, module owners MUST add the requirement type to the beginning of each parameter’s description. Below are the requirement types with a definition and example for the description decorator:

Parameter Requirement TypeDefinitionExample Description Decorator
RequiredThe parameter value must be provided. The parameter does not have a default value and hence the module expects and requires an input.@description('Required. <PARAMETER DESCRIPTION HERE...>')
ConditionalThe parameter value can be optional or required based on a condition, mostly based on the value provided to other parameters. Should contain a sentence starting with ‘Required if (…).’ to explain the condition.@description('Conditional. <PARAMETER DESCRIPTION HERE...>')
OptionalThe parameter value is not mandatory. The module provides a default value for the parameter.@description('Optional. <PARAMETER DESCRIPTION HERE...>')
GeneratedThe parameter value is generated within the module and should not be specified as input in most cases. A common example of this is the utcNow() function that is only supported as the input for a parameter value, and not inside a variable.@description('Generated. <PARAMETER DESCRIPTION HERE...>')



See origin...

ID: BCPRMNFR2 - User-defined types - AVM-Common-Types

When implementing any of the Bicep interface variants you MUST import their User-defined type (UDT) via the published AVM-Common-Types module.

When doing so, each type MUST be imported separately, right above the parameter or output that uses it.

import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:*.*.*'
@description('Optional. Array of role assignments to create.')
param roleAssignments roleAssignmentType[]?
import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:*.*.*'
@description('Optional. The diagnostic settings of the service.')
param diagnosticSettings diagnosticSettingFullType[]?

Importing them individually as opposed to one common block has several benefits such as

  • Individual versioning of types
  • If you must update the version for one type, you’re not exposed to unexpected changes to other types
Important

The import (...) block MUST not be added in between a parameter’s definition and its metadata. Doing so breaks the metadata’s binding to the parameter in question.

Finally, you should check for version updates regularly to ensure the resource module stays consistent with the specs. If the used AVM-Common-Types runs stale, the CI may eventually fail the module’s static tests.




Testing

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR1Prescribed TestsMUSTOwnerContributorBAU
2SNFR2E2E TestingMUSTOwnerContributorBAU
3SNFR3AVM Compliance TestsMUSTOwnerContributorInitial
4SNFR4Unit TestsSHOULDOwnerContributorBAU
5SNFR5Upgrade TestsSHOULDOwnerContributorBAU
6SNFR6Static Analysis/Linting TestsMUSTOwnerContributorBAU
7SNFR7Idempotency TestsMUSTOwnerContributorBAU
8SNFR24Testing Child, Extension & Interface ResourcesMUSTOwnerContributorBAU
9BCPNFR10Test Bicep File NamingMUSTOwnerContributorBAU
10BCPNFR11Test ToolingMUSTOwnerContributorBAU
11BCPNFR12Deployment Test NamingMUSTOwnerContributorBAU
12BCPNFR13Test file metadataMUSTOwnerContributorBAU
13BCPNFR16Post-deployment testsMUSTOwnerContributorBAU
14BCPRMNFR1Expected Test DirectoriesMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: SNFR1 - Category: Testing - Prescribed Tests

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




See origin...

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.

Tip

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

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 & Bicep Log Analytics Workspace examples using simple/native declarations for use in E2E tests

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
  }
}
Skipping Deployments (SHOULD NOT)

Deployment tests are an important part of a module’s validation and a staple of AVM’s CI environment. However, there are situations where certain e2e-test-deployments cannot be performed against AVM’s test environment (e.g., if a special configuration/registration (such as certain AI models) is required). For these cases, the CI offers the possibility to ‘skip’ specific test cases by placing a file named .e2eignore in their test folder.

Note

A skipped test case is still added to the ‘Usage Examples’ section of the module’s readme and should be manually validated in regular intervals.

Details for use in E2E tests

You MUST add a note to the tests metadata description, which explains the excemption.

If you require that a test is skipped and add an “.e2eignore” file (e.g. \<module\>/tests/e2e/\<testname\>/.e2eignore) to a pull request, a member of the AVM Core Technical Bicep Team must approve set pull request. The content of the file is logged the module’s workflow runs and transparently communicates why the test case is skipped during the deployment validation stage. It iss hence important to specify the reason for skipping the deployment in this file.

Sample filecontent:

The test is skipped, as only one instance of this service can be deployed to a subscription.
Note

For resource modules, the ‘defaults’ and ‘waf-aligned’ tests can’t be skipped.

The deployment of a test can be skipped by adding a .e2eignore file into a test folder (e.g. /examples/<testname>).




See origin...

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.

Important

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.




See origin...

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.




See origin...

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.




See origin...

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.




See origin...

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.




See origin...

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

Module owners MUST test that child and extension resources and those Bicep or Terreform interface resources that are supported by their modules, are validated 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.




See origin...

ID: BCPNFR10 - Category: Testing - Test Bicep File Naming

Module owners MUST name their test .bicep files in the /tests/e2e/<defaults/waf-aligned/max/etc.> directories: main.test.bicep as the test framework (CI) relies upon this name.




See origin...

ID: BCPNFR11 - Category: Testing - Test Tooling

Module owners MUST use the below tooling for unit/linting/static/security analysis tests. These are also used in the AVM Compliance Tests.

  • PSRule for Azure
  • Pester
    • Some tests are provided as part of the AVM Compliance Tests, but you are free to also use Pester for your own tests.



See origin...

ID: BCPNFR12 - Category: Testing - Deployment Test Naming

Module owners MUST invoke the module in their test using the syntax:

module testDeployment '../../../main.bicep' =

Example 1: Working example with a single deployment

module testDeployment '../../../main.bicep' = {
  scope: resourceGroup
  name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
  params: {
    (...)
  }
}

Example 2: Working example using a deployment loop

@batchSize(1)
module testDeployment '../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
  scope: resourceGroup
  name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
  params: {
    (...)
  }
}]

The syntax is used by the ReadMe-generating utility to identify, pull & format usage examples.




See origin...

ID: BCPNFR13 - Category: Testing - Test file metadata

By default, the ReadMe-generating utility will create usage examples headers based on each e2e folder’s name.
Module owners MAY provide a custom name & description by specifying the metadata blocks name & description in their main.test.bicep test files.

For example:

metadata name = 'Using Customer-Managed-Keys with System-Assigned identity'
metadata description = 'This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.'

would lead to a header in the module’s readme.md file along the lines of

### Example 1: _Using Customer-Managed-Keys with System-Assigned identity_

This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.



See origin...

ID: BCPNFR16 - Category: Testing - Post-deployment tests

For each test case in the e2e folder, you can optionally add post-deployment Pester tests that are executed once the corresponding deployment completed and before the removal logic kicks in.

To leverage the feature you MUST:

  • Use Pester as a test framework in each test file

  • Name the file with the suffix "*.tests.ps1"

  • Place each test file the e2e test’s folder or any subfolder (e.g., e2e/max/myTest.tests.ps1 or e2e/max/tests/myTest.tests.ps1)

  • Implement an input parameter TestInputData in the following way:

    param (
        [Parameter(Mandatory = $false)]
        [hashtable] $TestInputData = @{}
    )

    Through this parameter you can make use of every output the main.test.bicep file returns, as well as the path to the test template file in case you want to extract data from it directly.

    For example, with an output such as output resourceId string = testDeployment[1].outputs.resourceId defined in the main.test.bicep file, the $TestInputData would look like:

    $TestInputData = @{
      DeploymentOutputs    = @{
        resourceId = @{
          Type  = "String"
          Value = "/subscriptions/***/resourceGroups/dep-***-keyvault.vaults-kvvpe-rg/providers/Microsoft.KeyVault/vaults/***kvvpe001"
        }
      }
      ModuleTestFolderPath = "/home/runner/work/bicep-registry-modules/bicep-registry-modules/avm/res/key-vault/vault/tests/e2e/private-endpoint"
    }

    A full test file may look like:

    ➕ Pester post-deployment test file example
    param (
        [Parameter(Mandatory = $false)]
        [hashtable] $TestInputData = @{}
    )
    
    Describe 'Validate private endpoint deployment' {
    
        Context 'Validate sucessful deployment' {
    
            It "Private endpoints should be deployed in resource group" {
    
                $keyVaultResourceId = $TestInputData.DeploymentOutputs.resourceId.Value
                $testResourceGroup = ($keyVaultResourceId -split '\/')[4]
                $deployedPrivateEndpoints = Get-AzPrivateEndpoint -ResourceGroupName $testResourceGroup
                $deployedPrivateEndpoints.Count | Should -BeGreaterThan 0
            }
        }
    }



See origin...

ID: BCPRMNFR1 - Category: Testing - Expected Test Directories

Module owners MUST create the defaults, waf-aligned folders within their /tests/e2e/ directory in their resource module source code and SHOULD create a max folder also. Module owners CAN create additional folders as required. Each folder will be used as described for various test cases.

Note

If a module can deploy varying styles of the same resource, e.g., VMs can be Linux or Windows, each style should be tested as both defaults and waf-aligned. Each must then be used as suffixes in the directory name to denote the style, e.g., for a VM we would expect to see:

  • /tests/e2e/linux.defaults/main.test.bicep
  • /tests/e2e/linux.waf-aligned/main.test.bicep
  • /tests/e2e/windows.defaults/main.test.bicep
  • /tests/e2e/windows.waf-aligned/main.test.bicep

Defaults tests (MUST)

The defaults folder contains a test instance that deploys the module with the minimum set of required parameters.

This includes input parameters of type Required plus input parameters of type Conditional marked as required for WAF compliance.

This instance has heavy reliance on the default values for other input parameters. Parameters of type Optional SHOULD NOT be used.

WAF aligned tests (MUST)

The waf-aligned folder contains a test instance that deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.

This includes input parameters of type Required, parameters of type Conditional marked as required for WAF compliance, and parameters of type Optional useful for WAF compliance.

Parameters and dependencies which are not needed for WAF compliance, SHOULD NOT be included.

Max tests (SHOULD)

The max folder contains a test instance that deploys the module using a large parameter set, enabling most of the modules’ features.

The purpose of this instance is primarily parameter validation and not necessarily to serve as a real example scenario. Ideally, all features, extension resources and child resources should be enabled in this test, unless not possible due to conflicts, e.g., in case parameters are mutually exclusive.

Note

Please note that this test is not mandatory to have, but recommended for bulk parameter validation. It can be skipped in case the module parameter validation is covered already by additional, more scenario-specific tests.

Additional tests (CAN)

Additional folders CAN be created by module owners as required.

For example, to validate parameters not covered by the max test due to conflicts, or to provide a real example scenario for a specific use case.




Documentation

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR15Automatic Documentation GenerationMUSTOwnerContributorBAU
2SNFR16Examples/E2EMUSTOwnerContributorBAU
3BCPNFR2Module Documentation GenerationMUSTOwnerContributorBAU
4BCPNFR3Usage Example formatsMUSTOwnerContributorBAU
5BCPNFR4Parameter Input ExamplesMAYOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.




See origin...

ID: SNFR16 - Category: Documentation - Examples/E2E

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




See origin...

ID: BCPNFR2 - Category: Documentation - Module Documentation Generation

Note

This script/tool is currently being developed by the AVM team and will be made available very soon.

Bicep modules documentation MUST be automatically generated via the provided script/tooling from the AVM team, providing the following headings:

  • Title
  • Description
  • Navigation
  • Resource Types
  • Usage Examples
  • Parameters
  • Outputs
  • Cross-referenced modules



See origin...

ID: BCPNFR3 - Category: Documentation - Usage Example formats

Usage examples for Bicep modules MUST be provided in the following formats:

  • Bicep file (orchestration module style) - .bicep

    module <resourceName> 'br/public:avm/[res|ptn|utl]/<publishedModuleName>:>version<' = {
      name: '${uniqueString(deployment().name, location)}-test-<uniqueIdentifier>'
      params: { (...) }
    }
  • JSON / ARM Template Parameter Files - .json

    {
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
      "contentVersion": "1.0.0.0",
      "parameters": { (...) }
    }
Note

The above formats are currently automatically taken & generated from the tests/e2e tests. It is enough to run the Set-ModuleReadMe or Set-AVMModule functions (from the utilities folder) to update the usage examples in the readme(s).

Note

Bicep Parameter Files (.bicepparam) are being reviewed and considered by the AVM team for the usability and features at this time and will likely be added in the future.




See origin...

ID: BCPNFR4 - Category: Documentation - Parameter Input Examples

Bicep modules MAY provide parameter input examples for parameters using the metadata.example property via the @metadata() decorator.

Example:

@metadata({
  example: 'uksouth'
})
@description('Optional. Location for all resources.')
param location string = resourceGroup().location

@metadata({
  example: '''
  {
    keyName: 'myKey'
    keyVaultResourceId: '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/my-rg/providers/Microsoft.KeyVault/vaults/myvault'
    keyVersion: '6d143c1a0a6a453daffec4001e357de0'
    userAssignedIdentityResourceId '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentity'
  }
  '''
})
@description('Optional. The customer managed key definition.')
param customerManagedKey customerManagedKeyType

It is planned that these examples are automatically added to the module readme’s parameter descriptions when running either the Set-ModuleReadMe or Set-AVMModule scripts (available in the utilities folder).




Release / Publishing

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR17Semantic VersioningMUSTOwnerContributorBAU
2SNFR18Breaking ChangesSHOULDOwnerContributorBAU
3SNFR19Registries TargetedMUSTOwnerContributorBAU
4SNFR21Cross Language CollaborationSHOULDOwnerContributorBAU
5BCPNFR22Bicep Module ChangelogMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: SNFR17 - Category: Release - Semantic Versioning

Important

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

Module versioning before first Major version release 1.0.0

  • Initially modules MUST be released as version 0.1.0 and incremented via Minor and Patch versions only until the AVM Core Team are confident the AVM specifications are mature enough and appropriate CI test coverage is in place, plus the module owner is happy the module has been “road tested” and is now stable enough for its first Major release of version 1.0.0.

    Note

    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. 👍

  • Until first Major version 1.0.0 is released, given a version number X.Y.Z:

    • X Major version MUST NOT be bumped.
    • Y Minor version MUST be bumped when introducing breaking changes (which would normally bump Major after 1.0.0 release) or feature updates (same as it will be after 1.0.0 release).
    • Z Patch version MUST be bumped when introducing non-breaking, backward compatible bug fixes (same as it will be after 1.0.0 release).



See origin...

ID: SNFR18 - Category: Release - Breaking Changes

A module SHOULD avoid breaking changes, e.g., deprecating inputs vs. removing. If you need to implement changes that cause a breaking change, the major version should be increased.

Info

Modules that have not been released as 1.0.0 may introduce breaking changes, as explained in the previous ID SNFR17. That means that you have to introduce non-breaking and breaking changes with a minor version jump, as long as the module has not reached version 1.0.0.

There are, however, scenarios where you want to include breaking changes into a commit and not create a new major version. If you want to introduce breaking changes as part of a minor update, you can do so. In this case, it is essential to keep the change backward compatible, so that the existing code will continue to work. At a later point, another update can increase the major version and remove the code introduced for the backward compatibility.

Tip

See the language specific examples to find out how you can deal with deprecations in AVM modules.




See origin...

ID: SNFR19 - Category: Publishing - Registries Targeted

Modules MUST be published to their respective language public registries.

Tip

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




See origin...

ID: SNFR21 - Category: Publishing - Cross Language Collaboration

When the module owners of the same Resource, Pattern or Utility 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.




See origin...

ID: BCPNFR22 - Category: Publishing - Changelog

When a module to be published (i.e., that has a version.json file) is changed, an entry MUST be created in the CHANGELOG.md file in the module folder. A link to the latest version of the changelog file has to be included at the top of the file, just below the # Changelog line. It is surrounded by empty lines.

# Changelog

The latest version of the changelog can be found [here](https://github.com/Azure/bicep-registry-modules/blob/main/avm/<ptn|res|utl>/<namespace/modulename[/submodulePath]>/CHANGELOG.md).

For each new version, an entry MUST be created above all existing versions in the CHANGELOG.md file of the module.

## <version>

### Changes

- This changed
- And this also

### Breaking Changes

- None

Each version’s entry:

  • MUST contain two sections: Changes and Breaking Changes. At least one of them must have a meaningful entry and sections must not be left empty. A - None may be added as content for a section.
  • MUST exist only once.
  • All versions appear in descending order, which puts the most recent changes at the top.

What SHOULD be listed in the (Breaking) Changes section:

  • Relevant changes for the module
  • Changes in tests do not need to be added
Note

The versioning is following the SNFR17 - Semantic Versioning spec.

Example content of the CHANGELOG.md

A CHANGELOG.md file in the module’s root folder MUST start with the # Changelog header, followed by an empty line and a link to the latest published version of the changelog file, followed by another empty line. A section for each published version follows. Newer versions are placed above older versions.

# Changelog

The latest version of the changelog can be found [here](https://github.com/Azure/bicep-registry-modules/blob/main/avm/res/aad/domain-service/CHANGELOG.md).

## 0.2.1

### Changes

- Updated the referenced AVM common types

### Breaking Changes

- None

## 0.2.0

### Changes

- Implemented the minCPU parameter
- Updated the referenced VirtualNetwork module
- Updated the referenced AVM common types

### Breaking Changes

- The minCPU parameter is mandatory

## 0.1.0

### Changes

- Initial Release

### Breaking Changes

- None

Each bullet point should start with a capital letter.

Manual Editing

It is possible to modify the changelog content any time, e.g., to add missing versions, which will not create a new release of the module itself. Please note the following requirements in all cases:

  • All versions in the file, need to be valid and available as published version
  • Every version needs the two sections ## Changes and ## Breaking Changes with content
Note

Azure Verified Modules are artifacts in the Microsoft Container Registry (MCR). Every version of a module exists as a tag in the Container Registry and can be listed as tags for each module https://mcr.microsoft.com/v2/bicep/avm/(res|ptn|utl)/<namespace/modulename>/tags/list




Code Style

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1BCPNFR8Code Styling - lower camelCasingSHOULDOwnerContributorBAU
2BCPNFR17Code Styling - Type castingSHOULDOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: BCPNFR8 - Category: Composition - Code Styling - lower camelCasing

Module owners SHOULD use lower camelCasing for naming the following:

  • Parameters
  • Variables
  • Outputs
  • User Defined Types
  • Resources (symbolic names)
  • Modules (symbolic names)

For example: camelCasingExample (lowercase first word (entirely), with capital of first letter of all other words and rest of word in lowercase)




See origin...

ID: BCPNFR17 - Category: Composition - Code Styling - Type casting

To improve the usability of primitive module properties declared as strings, you SHOULD declare them using a type which better represents them, and apply any required casting in the module on behalf of the user.

For reference, please refer to the following examples:

Boolean as String

Boolean as String
@allowed([
  'false'
  'true'
])
param myParameterValue string = 'false'

resource myResource '(...)' = {
  (...)
  properties: {
    myParameter: myParameterValue
  }
}
param myParameterValue string = false

resource myResource '(...)' = {
  (...)
  properties: {
    myParameter: string(myParameterValue)
  }
}

Integer Array as String Array

Integer Array as String Array
@allowed([
  '1'
  '2'
  '3'
])
param zones array

resource myResource '(...)' = {
  (...)
  properties: {
    zones: zones
  }
}
@allowed([
  1
  2
  3
])
param zones int[]

resource myResource '(...)' = {
  (...)
  properties: {
    zones: map(zones, zone => string(zone))
  }
}



Bicep Utility Module Specifications

Contribution / Support

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR8Module Owner(s) GitHubMUSTOwnerInitial
2SNFR20GitHub Teams OnlyMUSTOwnerInitial
3SNFR9AVM & PG Teams GitHub Repo PermissionsMUSTOwnerInitial
4SNFR10MIT LicensingMUSTOwnerInitial
5SNFR11Issues Response TimesMUSTOwnerContributorBAU
6SNFR12Versions SupportedMUSTOwnerBAU
7SNFR23GitHub Repo LabelsMUSTOwnerBAU
8BCPNFR15AVM Module Issue template fileMUSTOwnerBAU
➕ See Specifications for this category
See origin...

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.

Note

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.




See origin...

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 a GitHub team assigned for module owners. This team MUST be created in the Azure organization in GitHub.

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

Info

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.

Bicep

Important

As part of the module proposal process, the name of the GitHub team for each approved module is already defined in the respective Module Indexes (or CSV file). This team MUST be created (and used) for each module.

Module owners don’t need to construct the name of the GitHub team for their module themselves, instead they need use the name prescribed in the related CSV file, at the time of approval.

For a direct link, see the list of related index pages:

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!

Naming Convention

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

  • <hyphenated module name>-module-owners-bicep - to grant permissions for module owners on Bicep modules

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 == the role the GitHub Team is assigned to
  • <bicep == the language the module is written in

Examples:

  • avm-res-compute-virtualmachine-module-owners-bicep
Note

The naming convention for Bicep modules is slightly different than the naming convention for their respective GitHub teams.

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.

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

Grant permissions through team memberships

Note

In case of Bicep modules, permissions to the BRM repository (the repo of the Bicep Registry) are granted via assigning the -module-owners- 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 team to the respective parent, only the AVM core team can approve this parent-child relationship.

Module owners MUST create their -module-owners- team and as part of the provisioning process, they MUST request the addition of this team to its respective parent team (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.

Example - GitHub team 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.
Tip

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).

Note

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 Bicep reviewers, with the @Azure/ prefix, i.e., @Azure/avm-module-reviewers-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-module-reviewers-bicep

Terraform

Note

Access management for Terraform repositories now uses a single team, membership of which is managed using an internal entitlement management tool (Core Identity).

All module owners MUST request access to the avm-module-owners-terraform GitHub team via the Azure Verified Module Owners Terraform entitlement in Core Identity (Microsoft internal tool).




See origin...

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

Note

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

Terraform

Important

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.




See origin...

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

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




See origin...

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

A module owner MUST respond to logged issues as defined in the support statement. See Module Support for more information.




See origin...

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.




See origin...

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:

➕ AVM Standard GitHub 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 📣In the AVM repository: this module needs an owner to develop or maintain it. In the BRM repository: the module owner needs to review a PR.
FF0019
Needs: Module Contributor 📣This module needs secondary owner(s) or contributor(s) to develop or maintain it
C95474
Needs: Core Team 🧞‍♂️This item needs the AVM Core Team to review it
DB4503
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 Deprecated 🔴This is a request to deprecate a module
000000
Status: Module Orphaned 🟡The module has no owner and is therefore orphaned at this time
F4A460
Status: Ready For Repository Creation 📝This module is approved and the owner is ready for the repository to be created (Terraform)
136A41
Status: Repository Created 📄This module has had it's repository created and configured ready for owner contribution (Terraform)
27AB03
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: CI 🚀This issue is related to the AVM CI
74CFB0
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
Class: Utility Module 📦This is a utility module
CAD1DE
Class: Child Module 📦This is a child module
5E5186

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

➕ Set-AvmGitHubLabels.ps1

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
```shell
# 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
  



See origin...

ID: BCPNFR15 - Category: Contribution/Support - AVM Module Issue template file

Module owners MUST add an entry to the AVM Module Issue template file in the BRM repository (here). When the module is deprecated, this entry MUST be removed from the file.

Note

Through this approach, the AVM core team will allow raising a bug or feature request for a module, only after the module gets merged to the BRM repository.

The module name entry MUST be added to the dropdown list with id module-name-dropdown as an option, in alphabetical order.

Important

Module owners MUST ensure that the module name is added in alphabetical order, to simplify selecting the right module name when raising an AVM module issue.

Example - AVM Module Issue template module name entry for the Bicep resource module of Azure Virtual Network (avm/res/network/virtual-network):

- type: dropdown
  id: module-name-dropdown
  attributes:
    label: Module Name
    description: Which existing AVM module is this issue related to?
    options:
      ...
      - "avm/res/network/virtual-network"
      ...



Telemetry

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR3Deployment/Usage TelemetryMUSTOwnerInitial
2SFR4Telemetry Enablement FlexibilityMUSTOwnerInitial
3BCPFR4Telemetry EnablementMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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

Modules MUST provide the capability to collect deployment/usage telemetry as detailed in Telemetry further.

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.)

Telemetry Information Notice

Note

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

  • Bicep: Using the 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.

Module Class Applicability

This specification applies to all AVM module classes (resource, pattern, utility), however, in case of utility modules, telemetry collection MUST only be added when the utility module deploys any resources (e.g., a deployment script resource). If the utility module does not deploy any resources, telemetry collection MUST NOT be added.

Bicep

Important

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.

The value you need to use for your module is defined in the related module index. You can look it up on the index pages for Resource Modules, Pattern Modules and Utility Modules.

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

  • <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-
    • The prefixes: avm-ptn-
  • <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)
Note

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.

An example deployment name for the AVM Virtual Machine Resource Module would be: 46d3xbcp.res.compute-virtualmachine.1-2-3.eum3

An example deployment name for a shortened module name would be: 46d3xbcp.res.desktopvirtualization-appgroup.1-2-3.eum3

Tip

Terraform: Terraform uses a telemetry provider, the configuration of which is the same for every module and is included in the template repo.

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

Terraform

To enable telemetry data collection for Terraform modules, the modtm telemetry provider MUST be used. 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.

The modtm provider MUST be 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"
    }
  }
}



See origin...

ID: SFR4 - Category: Telemetry - Telemetry Enablement Flexibility

The telemetry collection MUST be on/enabled by default, however module consumers MUST be allowed to disable it by setting the below parameter/variable value to false:

  • Bicep: enableTelemetry
  • Terraform: enable_telemetry
Note

Whenever a module references AVM modules that implement the telemetry parameter (e.g., a pattern module that uses AVM resource modules), the telemetry parameter value MUST be passed through to these modules. This is necessary to ensure a consumer can reliably enable & disable the telemetry feature for all used modules.

This general specification can be modified for some use-cases, that are language specific:

Bicep

For cross-references in resource modules, the spec BCPFR7 also applies.

Terraform

Currently, no further requirements apply.




See origin...

ID: BCPFR4 - Category: Composition - Telemetry Enablement

To comply with specifications outlined in SFR3 & SFR4 you MUST incorporate the following code snippet into your modules. Place this code sample in the “top level” main.bicep file; it is not necessary to include it in any nested Bicep files (child modules), unless they are marked for direct publishing (Ref Child module publishing).

@description('Optional. Location for all resources.')
param location string = resourceGroup().location

@description('Optional. Enable/Disable usage telemetry for module.')
param enableTelemetry bool = true

#disable-next-line no-deployments-resources
resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) {
  name: take('46d3xbcp.res.compute-virtualmachine.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}', 64)
  properties: {
    mode: 'Incremental'
    template: {
      '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
      contentVersion: '1.0.0.0'
      resources: []
      outputs: {
        telemetry: {
          type: 'String'
          value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
        }
      }
    }
  }
}



Naming / Composition

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR1Preview ServicesMUSTOwnerBAU
2SFR2WAF AlignedSHOULDOwnerBAU
3SNFR25Resource NamingMUSTOwnerInitial
4UMNFR1Module NamingMUSTOwnerInitial
5BCPFR1Cross-Referencing ModulesMAYOwnerContributorBAU
6BCPNFR19User-defined types - NamingMUSTOwnerContributorBAU
7BCPNFR23Module compositionMUSTOwnerContributorBAU
8BCPNFR24Deterministic Deployment NamesMUSTOwnerContributorBAU
9BCPNFR14VersioningMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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”



See origin...

ID: SFR2 - Category: Composition - WAF Aligned

Modules SHOULD set defaults in input parameters/variables to align to high priority/impact/severity 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.

Tip

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




See origin...

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>.

Tip

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.




See origin...

ID: UMNFR1 - Category: Naming - Module Naming

Utility Modules MUST follow the below naming conventions (all lower case).

Important

As part of the module proposal process, the module’s approved name is captured both in the module proposal issue AND the related module index page (backed by the corresponding CSV file).

Therefore, module owners don’t need to construct the module’s name themselves, instead they need use the name prescribed in the module proposal issue or in the related CSV file, at the time of approval.

Bicep Utility Module Naming

  • Naming convention: avm/utl/<hyphenated grouping/category name>/<hyphenated utility module name>
  • Example: avm/utl/general/get-environment or avm/utl/types/avm-common-types
  • Segments:
    • utl defines this as a utility module
    • <hyphenated grouping/category name> is a hierarchical grouping of utility modules by category, with each word separated by dashes, such as: general or types
    • <hyphenated utility module name> is a term describing the module’s function, with each word separated by dashes, e.g., get-environment = to get environmental details; avm-common-types = to use common types.

Terraform Utility Module Naming

  • Naming convention:
    • avm-utl-<utility module name> (Module name for registry)
    • terraform-<provider>-avm-utl-<utility module name> (GitHub repository name to meet registry naming requirements)
  • Example: avm-utl-sku-finder or avm-utl-naming
  • 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.
    • utl defines this as a utility module
    • <utility module name> is a term describing the module’s function, e.g., sku-finder = to find available SKUs; naming = to handle naming conventions.



See origin...

ID: BCPFR1 - Category: Composition - Cross-Referencing Modules

Module owners MAY cross-reference other modules to build either Resource or Pattern modules.

However, they MUST be referenced only by a public registry reference to a pinned version e.g. br/public:avm/[res|ptn|utl]/<publishedModuleName>:>version<. They MUST NOT use local parent path references to a module e.g. ../../xxx/yyy.bicep.

The only exception to this rule are child modules as documented in BCPFR6.

Modules MUST NOT contain references to non-AVM modules.




See origin...

ID: BCPNFR19 - User-defined types - Naming

User-defined types (UDTs) MUST always end with the suffix (...)Type to make them obvious to users. In addition it is recommended to extend the suffix to (...)OutputType if a UDT is exclusively used for outputs.

type subnet = { ... } // Wrong
type subnetType = { ... } // Correct
type subnetOutputType = { ... } // Correct, if used only for outputs

Since User-defined types (UDTs) MUST always be singular as per BCPNFR18, their naming should reflect this and also be singular.

type subnetsType = { ... } // Wrong
type subnetType = { ... } // Correct



See origin...

ID: BCPNFR23 - Category: Composition

Each Bicep AVM module that lives within the Azure/bicep-registry-modules (BRM) repository in the avm directory MUST have the following directories and files:

  • /tests - (for unit tests and additional E2E/integration if required - e.g. Pester etc.)
    • /e2e - (all examples must deploy successfully - these will be used to automatically generate the examples in the README.md for the module)
  • /src - (for scripts and other files - e.g., scripts used by the template)
    • exampleFile.ps1
  • /modules - (for sub-modules only if used and NOT children of the primary resource - e.g. RBAC role assignments)
    • exampleTemplate.bicep
  • /main.bicep (AVM Module main .bicep file and entry point/orchestration module)
  • /main.json (auto generated and what is published to the MCR via BRM)
  • /version.json (BRM requirement)
  • /README.md (auto generated AVM Module documentation)
  • /CHANGELOG.md (manually maintained changelog file with one entry per published version)

Directory and File Structure Example

/ Root of Azure/bicep-registry-modules
├───avm
│   ├───ptn
│   │   └───apptiervmss
│   │       │   main.bicep
│   │       │   main.json
│   │       │   README.md
│   │       │   CHANGELOG.md
│   │       │   version.json
│   │       ├───src (optional)
│   │       │   ├───Get-Cake.ps1
│   │       │   └───Find-Waldo.ps1
│   │       ├───modules (optional)
│   │       │   ├───helper.bicep
│   │       │   └───role-assignment.bicep
│   │       └───tests
│   │           ├───unit (optional)
│   │           └───e2e
│   │               ├───defaults
│   │               ├───waf-aligned
│   │               └───max
│   │
│   └───res
│       └───compute
│           └───virtual-machine
│               │   main.bicep
│               │   main.json
│               │   README.md
│               │   CHANGELOG.md
│               │   version.json
│               ├───src (optional)
│               │   ├───Set-Bug.ps1
│               │   └───Invoke-Promotion.ps1
│               ├───modules (optional)
│               │   ├───helper.bicep
│               │   └───role-assignment.bicep
│               └───tests
│                   ├───unit (optional)
│                   └───e2e
│                       ├───defaults
│                       ├───waf-aligned
│                       └───max
├───other repo dirs...
└───other repo files...



See origin...

ID: BCPNFR24 - Category: Naming/Composition - Deterministic Deployment Names

When a module references child, utility, or other modules, the deployment name MUST be deterministic. This means the deployment name must produce the same value for the same set of inputs across repeated deployments.

Why deterministic?

Azure Resource Manager has an 800-deployment limit per scope (resource group, subscription, management group, tenant). Non-deterministic names (e.g., those incorporating timestamps or utcNow()) create a new deployment object on every run, which can lead to this limit being reached over time.

While an automatic cleanup process exists for resource group and subscription scopes, it can take some time to take effect. Due to eventual consistency in the backend, the deployment count may not reflect the cleanup immediately, which can lead to failed deployments even when the actual number of deployments is below the 800 limit. Additionally, automatic cleanup does not apply to management group or tenant scopes.

We are actively working with the product team to enhance the cleanup process. In the meantime, deterministic deployment names provide a reliable way to keep deployment counts stable by overwriting previous deployment objects rather than creating new ones.

Deterministic deployment names cause Azure to overwrite the previous deployment object, keeping the deployment count stable regardless of how many times the module is deployed.

Requirement

Module owners MUST construct deployment names for referenced modules using uniqueString() seeded with the parent resource’s ID (<parentResource>.id) and location, rather than deployment().name, subscription().id, resourceGroup().id, utcNow(), or other non-deterministic or scope-level values.

The deployment name MUST follow the pattern:

'${uniqueString(<parentResource>.id, location)}-<ChildModuleDescriptor>-${index}'

Where:

SegmentDescription
uniqueString(<parentResource>.id, location)A deterministic hash derived from the parent resource’s resource ID and deployment location. This is both unique per resource instance and stable across deployments.
<ChildModuleDescriptor>A short, human-readable label identifying the child module being deployed (e.g., DB, Subnet, FederatedIdentityCred).
${index}The loop index variable, included when deploying in a loop. Omit for single (non-looped) deployments.
location parameter

If location is not available, for example when deploying a global resource that does not have a location property, it is acceptable to omit it. However, the <parentResource>.id MUST always be included as the primary seed for uniqueString.

Why parent resource ID?

Using the parent resource’s ID as the uniqueString seed provides two critical properties:

  1. Deterministic — the same parent resource always produces the same hash, so repeated deployments overwrite rather than accumulate.
  2. Collision-free — different parent resource instances produce different hashes, so deploying multiple instances of the same module type within the same scope does not cause naming collisions.
Why not subscription().id and resourceGroup().id separately?

The parent resource’s ID (e.g., /subscriptions/.../resourceGroups/.../providers/.../resourceName) already contains the subscription ID and resource group ID as segments. Using <parentResource>.id as a single input to uniqueString captures all of this context in one value, keeping the code concise and readable rather than passing multiple scope-level values separately.

Supporting multiple deployments of the same module at the same scope

A common scenario is deploying the same module type more than once within the same scope — for example, two different SQL servers each with their own set of databases, or two user-assigned identities each with their own federated credentials. Because the parent resource ID is unique per resource instance, the resulting deployment names will differ even when the child module type and index are identical. This ensures that parallel deployments of the same module at the same scope do not collide.

Other approaches fail on one or both of these properties:

ApproachDeterministic?Collision-free?Issue
deployment().nameChanges every deployment; hits 800-limit
utcNow() / timestampsChanges every deployment; hits 800-limit
subscription().id + resourceGroup().idSame hash for all resources in the same RG; collisions when deploying multiple instances
<parentResource>.id, locationRecommended — stable and unique per instance

Examples

Example 1: Single child module deployment

resource server 'Microsoft.Sql/servers@2023-05-01-preview' = { ... }

module server_database 'database/main.bicep' = {
  name: '${uniqueString(server.id, location)}-Sql-DB'
  params: {
    serverName: server.name
    (...)
  }
}

Example 2: Child module deployment in a loop

resource server 'Microsoft.Sql/servers@2023-05-01-preview' = { ... }

module server_databases 'database/main.bicep' = [for (database, index) in (databases ?? []): {
  name: '${uniqueString(server.id, location)}-Sql-DB-${index}'
  params: {
    serverName: server.name
    (...)
  }
}]



See origin...

ID: BCPNFR14 - Category: Composition - Versioning

To meet SNFR17 and depending on the changes you make, you may need to bump the version in the version.json file.

  {
    "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
    "version": "0.1"
  }
  

The version value is in the form of MAJOR.MINOR. The PATCH version will be incremented by the CI automatically when publishing the module to the Public Bicep Registry once the corresponding pull request is merged. Therefore, contributions that would only require an update of the patch version, can keep the version.json file intact.

For example, the version value should be:

  • 0.1 for new modules, so that they can be released as v0.1.0.
  • 1.0 once the module owner signs off the module is stable enough for it’s first Major release of v1.0.0.
  • 0.x for all feature updates between the first release v0.1.0 and the first Major release of v1.0.0.



Inputs / Outputs

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR14Data TypesSHOULDOwnerContributorBAU
2SNFR22Parameters/Variables for Resource IDsMUSTOwnerContributorBAU
3SNFR26Output - Parameters - DecoratorsMUSTOwnerContributorBAU
4BCPNFR1Complex data types - GeneralMUSTOwnerContributorBAU
5BCPNFR9Inputs - DecoratorsMUSTOwnerContributorBAU
6BCPNFR18User-defined types - SpecificationMUSTOwnerContributorBAU
7BCPNFR19User-defined types - NamingMUSTOwnerContributorBAU
8BCPNFR20User-defined types - ExportMUSTOwnerContributorBAU
9BCPNFR21User-defined types - DecoratorsMUSTOwnerContributorBAU
10BCPNFR7Parameter Requirement TypesMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.




See origin...

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}, SHOULD contain ResourceId/resource_id in its parameter/variable name when that parameter/variable is part of a user-defined type. This assists 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 a user-defined type: in Bicep its parameter name should be workspaceResourceId and the variable name in Terraform should be workspace_resource_id.

In that user-defined context, workspaceId is not descriptive enough and is ambiguous as to which ID is required to be input.

Special considerations for Bicep

If the property is nested in a parameter and you opt for a resource-derived type (that is, a schema defined by the resource provider), this requirement does not apply. We do however recommend to use a user-defined type whenever these cases occur to increase the module’s usability.

Example for the property subnetArmId of the Cognitive Service’s property networkInjections:

If using a user-defined type, you may define a type for the networkInjections parameter like

param networkInjections networkInjectionType?

@export()
type networkInjectionType = {
  subnetResourceId: string

  // (...)
}

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: [{
      subnetArmId: networkInjections.?subnetResourceId
      // (...)
    }]
  }
}

or a resource-derived type like

param networkInjections resourceInput<'Microsoft.CognitiveServices/accounts@2025-06-01'>.properties.networkInjections

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: networkInjections
  }
}



See origin...

ID: SNFR26 - Output-Parameters - Decorators

Output parameters MUST implement:

Output parameters
@description('The resourceId of your resource.')
output sampleResourceId string = sampleResource.id

@description('The key of your resource.')
@secure()
output sampleResourceKey string = sampleResource.key
# Resource output
output "foo" {
  description = "MyResource foo attribute"
  value = azurerm_resource_myresource.foo
}

# Output of a sensitive attribute
output "bar" {
  description = "MyResource bar attribute"
  value     = azurerm_resource_myresource.bar
  sensitive = true
}



See origin...

ID: BCPNFR1 - Category: Inputs - Complex data types - General

To simplify the consumption experience for module consumers when interacting with complex data types input parameters, mainly objects and arrays, the Bicep features of Resource-Derived Types or User-Defined Types MUST be used and declared.

Tip

User-Defined Types are GA in Bicep as of version v0.21.1, Resource-Derived Types are GA as of version v0.34.1, please ensure you have this version(s) installed as a minimum.

Resource-Derived Types and User-Defined Types allow intellisense support in supported IDEs (e.g. Visual Studio Code) for complex input parameters using objects and array of objects.

v0.x Exemption

While we allow the release of major versions, starting with v1.0.0, retrofitting Resource-Derived Types and User-Defined Types for all modules will take a considerable amount of time.

Therefore, the addition of these features is currently NOT mandated/enforced. However, all modules MUST implement Resource-Derived Types and User-Defined Types prior to the release of their v1.0.0 version.




See origin...

ID: BCPNFR9 - Inputs - Decorators

Similar to BCPNFR21, input parameters MUST implement decorators such as description & secure (if sensitive).

Further, input parameters SHOULD implement decorators like allowed, minValue, maxValue, minLength & maxLength (and others if available) as they have a big positive impact on the module’s usability.

@description('Optional. The threshold of your resource.')
@minValue(1)
@maxValue(10)
param threshold: int?
@description('Required. The SKU of your resource.')
@allowed([
'Basic'
'Premium'
'Standard'
])
param sku string



See origin...

ID: BCPNFR18 - User-defined types - Specification

User-defined types (UDTs) MUST always be singular and non-nullable. The configuration of either should instead be done directly at the parameter or output that uses the type.

For example, instead of

param subnets subnetsType
type subnetsType = { ... }[]?

the type should be defined like

param subnets subnetType[]?
type subnetType = { ... }

The primary reason for this requirement is clarity. If not defined directly at the parameter or output, a user would always be required to check the type to understand how e.g., a parameter is expected.




See origin...

ID: BCPNFR19 - User-defined types - Naming

User-defined types (UDTs) MUST always end with the suffix (...)Type to make them obvious to users. In addition it is recommended to extend the suffix to (...)OutputType if a UDT is exclusively used for outputs.

type subnet = { ... } // Wrong
type subnetType = { ... } // Correct
type subnetOutputType = { ... } // Correct, if used only for outputs

Since User-defined types (UDTs) MUST always be singular as per BCPNFR18, their naming should reflect this and also be singular.

type subnetsType = { ... } // Wrong
type subnetType = { ... } // Correct



See origin...

ID: BCPNFR20 - User-defined types - Export

User-defined types (UDTs) SHOULD always be exported via the @export() annotation in every template they’re implemented in.

@export()
type subnetType = { ... }

Doing so has the benefit that other (e.g., parent) modules can import them and as such reduce code duplication. Also, if the module itself is published, users of the Public Bicep Registry can import the types independently of the module itself. One example where this can be useful is a pattern module that may re-use the same interface when referencing a module from the registry.




See origin...

ID: BCPNFR21 - User-defined types - Decorators

Similar to BCPNFR9, User-defined types (UDTs) MUST implement decorators such as description & secure (if sensitive). This is true for every property of the UDT, as well as the UDT itself.

Further, User-defined types SHOULD implement decorators like allowed, minValue, maxValue, minLength & maxLength (and others if available) as they have a big positive impact on the module’s usability.

@description('My type''s description.')
type myType = {
  @description('Optional. The threshold of your resource.')
  @minValue(1)
  @maxValue(10)
  threshold: int?

  @description('Required. The SKU of your resource.')
  sku: ('Basic' | 'Premium' | 'Standard')
}



See origin...

ID: BCPNFR7 - Category: Inputs - Parameter Requirement Types

Modules will have lots of parameters that will differ in their requirement type (required, optional, etc.). To help consumers understand what each parameter’s requirement type is, module owners MUST add the requirement type to the beginning of each parameter’s description. Below are the requirement types with a definition and example for the description decorator:

Parameter Requirement TypeDefinitionExample Description Decorator
RequiredThe parameter value must be provided. The parameter does not have a default value and hence the module expects and requires an input.@description('Required. <PARAMETER DESCRIPTION HERE...>')
ConditionalThe parameter value can be optional or required based on a condition, mostly based on the value provided to other parameters. Should contain a sentence starting with ‘Required if (…).’ to explain the condition.@description('Conditional. <PARAMETER DESCRIPTION HERE...>')
OptionalThe parameter value is not mandatory. The module provides a default value for the parameter.@description('Optional. <PARAMETER DESCRIPTION HERE...>')
GeneratedThe parameter value is generated within the module and should not be specified as input in most cases. A common example of this is the utcNow() function that is only supported as the input for a parameter value, and not inside a variable.@description('Generated. <PARAMETER DESCRIPTION HERE...>')



Testing

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR1Prescribed TestsMUSTOwnerContributorBAU
2SNFR2E2E TestingMUSTOwnerContributorBAU
3SNFR3AVM Compliance TestsMUSTOwnerContributorInitial
4SNFR4Unit TestsSHOULDOwnerContributorBAU
5SNFR5Upgrade TestsSHOULDOwnerContributorBAU
6SNFR6Static Analysis/Linting TestsMUSTOwnerContributorBAU
7SNFR7Idempotency TestsMUSTOwnerContributorBAU
8BCPNFR10Test Bicep File NamingMUSTOwnerContributorBAU
9BCPNFR11Test ToolingMUSTOwnerContributorBAU
10BCPNFR12Deployment Test NamingMUSTOwnerContributorBAU
11BCPNFR13Test file metadataMUSTOwnerContributorBAU
12BCPNFR16Post-deployment testsMUSTOwnerContributorBAU
13BCPRMNFR1Expected Test DirectoriesMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: SNFR1 - Category: Testing - Prescribed Tests

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




See origin...

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.

Tip

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

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 & Bicep Log Analytics Workspace examples using simple/native declarations for use in E2E tests

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
  }
}
Skipping Deployments (SHOULD NOT)

Deployment tests are an important part of a module’s validation and a staple of AVM’s CI environment. However, there are situations where certain e2e-test-deployments cannot be performed against AVM’s test environment (e.g., if a special configuration/registration (such as certain AI models) is required). For these cases, the CI offers the possibility to ‘skip’ specific test cases by placing a file named .e2eignore in their test folder.

Note

A skipped test case is still added to the ‘Usage Examples’ section of the module’s readme and should be manually validated in regular intervals.

Details for use in E2E tests

You MUST add a note to the tests metadata description, which explains the excemption.

If you require that a test is skipped and add an “.e2eignore” file (e.g. \<module\>/tests/e2e/\<testname\>/.e2eignore) to a pull request, a member of the AVM Core Technical Bicep Team must approve set pull request. The content of the file is logged the module’s workflow runs and transparently communicates why the test case is skipped during the deployment validation stage. It iss hence important to specify the reason for skipping the deployment in this file.

Sample filecontent:

The test is skipped, as only one instance of this service can be deployed to a subscription.
Note

For resource modules, the ‘defaults’ and ‘waf-aligned’ tests can’t be skipped.

The deployment of a test can be skipped by adding a .e2eignore file into a test folder (e.g. /examples/<testname>).




See origin...

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.

Important

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.




See origin...

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.




See origin...

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.




See origin...

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.




See origin...

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.




See origin...

ID: BCPNFR10 - Category: Testing - Test Bicep File Naming

Module owners MUST name their test .bicep files in the /tests/e2e/<defaults/waf-aligned/max/etc.> directories: main.test.bicep as the test framework (CI) relies upon this name.




See origin...

ID: BCPNFR11 - Category: Testing - Test Tooling

Module owners MUST use the below tooling for unit/linting/static/security analysis tests. These are also used in the AVM Compliance Tests.

  • PSRule for Azure
  • Pester
    • Some tests are provided as part of the AVM Compliance Tests, but you are free to also use Pester for your own tests.



See origin...

ID: BCPNFR12 - Category: Testing - Deployment Test Naming

Module owners MUST invoke the module in their test using the syntax:

module testDeployment '../../../main.bicep' =

Example 1: Working example with a single deployment

module testDeployment '../../../main.bicep' = {
  scope: resourceGroup
  name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
  params: {
    (...)
  }
}

Example 2: Working example using a deployment loop

@batchSize(1)
module testDeployment '../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
  scope: resourceGroup
  name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
  params: {
    (...)
  }
}]

The syntax is used by the ReadMe-generating utility to identify, pull & format usage examples.




See origin...

ID: BCPNFR13 - Category: Testing - Test file metadata

By default, the ReadMe-generating utility will create usage examples headers based on each e2e folder’s name.
Module owners MAY provide a custom name & description by specifying the metadata blocks name & description in their main.test.bicep test files.

For example:

metadata name = 'Using Customer-Managed-Keys with System-Assigned identity'
metadata description = 'This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.'

would lead to a header in the module’s readme.md file along the lines of

### Example 1: _Using Customer-Managed-Keys with System-Assigned identity_

This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.



See origin...

ID: BCPNFR16 - Category: Testing - Post-deployment tests

For each test case in the e2e folder, you can optionally add post-deployment Pester tests that are executed once the corresponding deployment completed and before the removal logic kicks in.

To leverage the feature you MUST:

  • Use Pester as a test framework in each test file

  • Name the file with the suffix "*.tests.ps1"

  • Place each test file the e2e test’s folder or any subfolder (e.g., e2e/max/myTest.tests.ps1 or e2e/max/tests/myTest.tests.ps1)

  • Implement an input parameter TestInputData in the following way:

    param (
        [Parameter(Mandatory = $false)]
        [hashtable] $TestInputData = @{}
    )

    Through this parameter you can make use of every output the main.test.bicep file returns, as well as the path to the test template file in case you want to extract data from it directly.

    For example, with an output such as output resourceId string = testDeployment[1].outputs.resourceId defined in the main.test.bicep file, the $TestInputData would look like:

    $TestInputData = @{
      DeploymentOutputs    = @{
        resourceId = @{
          Type  = "String"
          Value = "/subscriptions/***/resourceGroups/dep-***-keyvault.vaults-kvvpe-rg/providers/Microsoft.KeyVault/vaults/***kvvpe001"
        }
      }
      ModuleTestFolderPath = "/home/runner/work/bicep-registry-modules/bicep-registry-modules/avm/res/key-vault/vault/tests/e2e/private-endpoint"
    }

    A full test file may look like:

    ➕ Pester post-deployment test file example
    param (
        [Parameter(Mandatory = $false)]
        [hashtable] $TestInputData = @{}
    )
    
    Describe 'Validate private endpoint deployment' {
    
        Context 'Validate sucessful deployment' {
    
            It "Private endpoints should be deployed in resource group" {
    
                $keyVaultResourceId = $TestInputData.DeploymentOutputs.resourceId.Value
                $testResourceGroup = ($keyVaultResourceId -split '\/')[4]
                $deployedPrivateEndpoints = Get-AzPrivateEndpoint -ResourceGroupName $testResourceGroup
                $deployedPrivateEndpoints.Count | Should -BeGreaterThan 0
            }
        }
    }



See origin...

ID: BCPRMNFR1 - Category: Testing - Expected Test Directories

Module owners MUST create the defaults, waf-aligned folders within their /tests/e2e/ directory in their resource module source code and SHOULD create a max folder also. Module owners CAN create additional folders as required. Each folder will be used as described for various test cases.

Note

If a module can deploy varying styles of the same resource, e.g., VMs can be Linux or Windows, each style should be tested as both defaults and waf-aligned. Each must then be used as suffixes in the directory name to denote the style, e.g., for a VM we would expect to see:

  • /tests/e2e/linux.defaults/main.test.bicep
  • /tests/e2e/linux.waf-aligned/main.test.bicep
  • /tests/e2e/windows.defaults/main.test.bicep
  • /tests/e2e/windows.waf-aligned/main.test.bicep

Defaults tests (MUST)

The defaults folder contains a test instance that deploys the module with the minimum set of required parameters.

This includes input parameters of type Required plus input parameters of type Conditional marked as required for WAF compliance.

This instance has heavy reliance on the default values for other input parameters. Parameters of type Optional SHOULD NOT be used.

WAF aligned tests (MUST)

The waf-aligned folder contains a test instance that deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.

This includes input parameters of type Required, parameters of type Conditional marked as required for WAF compliance, and parameters of type Optional useful for WAF compliance.

Parameters and dependencies which are not needed for WAF compliance, SHOULD NOT be included.

Max tests (SHOULD)

The max folder contains a test instance that deploys the module using a large parameter set, enabling most of the modules’ features.

The purpose of this instance is primarily parameter validation and not necessarily to serve as a real example scenario. Ideally, all features, extension resources and child resources should be enabled in this test, unless not possible due to conflicts, e.g., in case parameters are mutually exclusive.

Note

Please note that this test is not mandatory to have, but recommended for bulk parameter validation. It can be skipped in case the module parameter validation is covered already by additional, more scenario-specific tests.

Additional tests (CAN)

Additional folders CAN be created by module owners as required.

For example, to validate parameters not covered by the max test due to conflicts, or to provide a real example scenario for a specific use case.




Documentation

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR15Automatic Documentation GenerationMUSTOwnerContributorBAU
2SNFR16Examples/E2EMUSTOwnerContributorBAU
3BCPNFR2Module Documentation GenerationMUSTOwnerContributorBAU
4BCPNFR3Usage Example formatsMUSTOwnerContributorBAU
5BCPNFR4Parameter Input ExamplesMAYOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.




See origin...

ID: SNFR16 - Category: Documentation - Examples/E2E

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




See origin...

ID: BCPNFR2 - Category: Documentation - Module Documentation Generation

Note

This script/tool is currently being developed by the AVM team and will be made available very soon.

Bicep modules documentation MUST be automatically generated via the provided script/tooling from the AVM team, providing the following headings:

  • Title
  • Description
  • Navigation
  • Resource Types
  • Usage Examples
  • Parameters
  • Outputs
  • Cross-referenced modules



See origin...

ID: BCPNFR3 - Category: Documentation - Usage Example formats

Usage examples for Bicep modules MUST be provided in the following formats:

  • Bicep file (orchestration module style) - .bicep

    module <resourceName> 'br/public:avm/[res|ptn|utl]/<publishedModuleName>:>version<' = {
      name: '${uniqueString(deployment().name, location)}-test-<uniqueIdentifier>'
      params: { (...) }
    }
  • JSON / ARM Template Parameter Files - .json

    {
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
      "contentVersion": "1.0.0.0",
      "parameters": { (...) }
    }
Note

The above formats are currently automatically taken & generated from the tests/e2e tests. It is enough to run the Set-ModuleReadMe or Set-AVMModule functions (from the utilities folder) to update the usage examples in the readme(s).

Note

Bicep Parameter Files (.bicepparam) are being reviewed and considered by the AVM team for the usability and features at this time and will likely be added in the future.




See origin...

ID: BCPNFR4 - Category: Documentation - Parameter Input Examples

Bicep modules MAY provide parameter input examples for parameters using the metadata.example property via the @metadata() decorator.

Example:

@metadata({
  example: 'uksouth'
})
@description('Optional. Location for all resources.')
param location string = resourceGroup().location

@metadata({
  example: '''
  {
    keyName: 'myKey'
    keyVaultResourceId: '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/my-rg/providers/Microsoft.KeyVault/vaults/myvault'
    keyVersion: '6d143c1a0a6a453daffec4001e357de0'
    userAssignedIdentityResourceId '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentity'
  }
  '''
})
@description('Optional. The customer managed key definition.')
param customerManagedKey customerManagedKeyType

It is planned that these examples are automatically added to the module readme’s parameter descriptions when running either the Set-ModuleReadMe or Set-AVMModule scripts (available in the utilities folder).




Release / Publishing

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR17Semantic VersioningMUSTOwnerContributorBAU
2SNFR18Breaking ChangesSHOULDOwnerContributorBAU
3SNFR19Registries TargetedMUSTOwnerContributorBAU
4SNFR21Cross Language CollaborationSHOULDOwnerContributorBAU
5BCPNFR22Bicep Module ChangelogMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: SNFR17 - Category: Release - Semantic Versioning

Important

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

Module versioning before first Major version release 1.0.0

  • Initially modules MUST be released as version 0.1.0 and incremented via Minor and Patch versions only until the AVM Core Team are confident the AVM specifications are mature enough and appropriate CI test coverage is in place, plus the module owner is happy the module has been “road tested” and is now stable enough for its first Major release of version 1.0.0.

    Note

    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. 👍

  • Until first Major version 1.0.0 is released, given a version number X.Y.Z:

    • X Major version MUST NOT be bumped.
    • Y Minor version MUST be bumped when introducing breaking changes (which would normally bump Major after 1.0.0 release) or feature updates (same as it will be after 1.0.0 release).
    • Z Patch version MUST be bumped when introducing non-breaking, backward compatible bug fixes (same as it will be after 1.0.0 release).



See origin...

ID: SNFR18 - Category: Release - Breaking Changes

A module SHOULD avoid breaking changes, e.g., deprecating inputs vs. removing. If you need to implement changes that cause a breaking change, the major version should be increased.

Info

Modules that have not been released as 1.0.0 may introduce breaking changes, as explained in the previous ID SNFR17. That means that you have to introduce non-breaking and breaking changes with a minor version jump, as long as the module has not reached version 1.0.0.

There are, however, scenarios where you want to include breaking changes into a commit and not create a new major version. If you want to introduce breaking changes as part of a minor update, you can do so. In this case, it is essential to keep the change backward compatible, so that the existing code will continue to work. At a later point, another update can increase the major version and remove the code introduced for the backward compatibility.

Tip

See the language specific examples to find out how you can deal with deprecations in AVM modules.




See origin...

ID: SNFR19 - Category: Publishing - Registries Targeted

Modules MUST be published to their respective language public registries.

Tip

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




See origin...

ID: SNFR21 - Category: Publishing - Cross Language Collaboration

When the module owners of the same Resource, Pattern or Utility 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.




See origin...

ID: BCPNFR22 - Category: Publishing - Changelog

When a module to be published (i.e., that has a version.json file) is changed, an entry MUST be created in the CHANGELOG.md file in the module folder. A link to the latest version of the changelog file has to be included at the top of the file, just below the # Changelog line. It is surrounded by empty lines.

# Changelog

The latest version of the changelog can be found [here](https://github.com/Azure/bicep-registry-modules/blob/main/avm/<ptn|res|utl>/<namespace/modulename[/submodulePath]>/CHANGELOG.md).

For each new version, an entry MUST be created above all existing versions in the CHANGELOG.md file of the module.

## <version>

### Changes

- This changed
- And this also

### Breaking Changes

- None

Each version’s entry:

  • MUST contain two sections: Changes and Breaking Changes. At least one of them must have a meaningful entry and sections must not be left empty. A - None may be added as content for a section.
  • MUST exist only once.
  • All versions appear in descending order, which puts the most recent changes at the top.

What SHOULD be listed in the (Breaking) Changes section:

  • Relevant changes for the module
  • Changes in tests do not need to be added
Note

The versioning is following the SNFR17 - Semantic Versioning spec.

Example content of the CHANGELOG.md

A CHANGELOG.md file in the module’s root folder MUST start with the # Changelog header, followed by an empty line and a link to the latest published version of the changelog file, followed by another empty line. A section for each published version follows. Newer versions are placed above older versions.

# Changelog

The latest version of the changelog can be found [here](https://github.com/Azure/bicep-registry-modules/blob/main/avm/res/aad/domain-service/CHANGELOG.md).

## 0.2.1

### Changes

- Updated the referenced AVM common types

### Breaking Changes

- None

## 0.2.0

### Changes

- Implemented the minCPU parameter
- Updated the referenced VirtualNetwork module
- Updated the referenced AVM common types

### Breaking Changes

- The minCPU parameter is mandatory

## 0.1.0

### Changes

- Initial Release

### Breaking Changes

- None

Each bullet point should start with a capital letter.

Manual Editing

It is possible to modify the changelog content any time, e.g., to add missing versions, which will not create a new release of the module itself. Please note the following requirements in all cases:

  • All versions in the file, need to be valid and available as published version
  • Every version needs the two sections ## Changes and ## Breaking Changes with content
Note

Azure Verified Modules are artifacts in the Microsoft Container Registry (MCR). Every version of a module exists as a tag in the Container Registry and can be listed as tags for each module https://mcr.microsoft.com/v2/bicep/avm/(res|ptn|utl)/<namespace/modulename>/tags/list




Code Style

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1BCPNFR8Code Styling - lower camelCasingSHOULDOwnerContributorBAU
2BCPNFR17Code Styling - Type castingSHOULDOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: BCPNFR8 - Category: Composition - Code Styling - lower camelCasing

Module owners SHOULD use lower camelCasing for naming the following:

  • Parameters
  • Variables
  • Outputs
  • User Defined Types
  • Resources (symbolic names)
  • Modules (symbolic names)

For example: camelCasingExample (lowercase first word (entirely), with capital of first letter of all other words and rest of word in lowercase)




See origin...

ID: BCPNFR17 - Category: Composition - Code Styling - Type casting

To improve the usability of primitive module properties declared as strings, you SHOULD declare them using a type which better represents them, and apply any required casting in the module on behalf of the user.

For reference, please refer to the following examples:

Boolean as String

Boolean as String
@allowed([
  'false'
  'true'
])
param myParameterValue string = 'false'

resource myResource '(...)' = {
  (...)
  properties: {
    myParameter: myParameterValue
  }
}
param myParameterValue string = false

resource myResource '(...)' = {
  (...)
  properties: {
    myParameter: string(myParameterValue)
  }
}

Integer Array as String Array

Integer Array as String Array
@allowed([
  '1'
  '2'
  '3'
])
param zones array

resource myResource '(...)' = {
  (...)
  properties: {
    zones: zones
  }
}
@allowed([
  1
  2
  3
])
param zones int[]

resource myResource '(...)' = {
  (...)
  properties: {
    zones: map(zones, zone => string(zone))
  }
}



Terraform Specifications

Specifications by Category and Module Classification

CategoryResourcePatternUtility
Contribution/Support988
Telemetry222
Naming/Composition17127
CodeStyle292929
Inputs/Outputs865
Testing10109
Documentation444
Release/Publishing444
Summary837568

How to propose changes to the specifications?

Important

Any updates to existing or new specifications for Terraform must be submitted as a draft for review by Azure Terraform PG/Engineering(@Azure/terraform-avm) and AVM core team(@Azure/avm-core-team).

Important

Provider Versatility: Users have the autonomy to choose between AzureRM, AzAPI, or a combination of both, tailored to the specific complexity of module requirements.

What changed recently?

No specifications were changed in the last 30 days.

Subsections of Terraform

Terraform Interfaces

This chapter details the interfaces/schemas for the AVM Resource Modules features/extension resources as referenced in RMFR4 and RMFR5.

Diagnostic Settings

Important

Allowed values for logs and metric categories or category groups MUST NOT be specified to keep the module implementation evergreen for any new categories or category groups added by RPs, without module owners having to update a list of allowed values and cut a new release of their module.

  variable "diagnostic_settings" {
    type = map(object({
      name                                     = optional(string, null)
      log_categories                           = optional(set(string), [])
      log_groups                               = optional(set(string), ["allLogs"])
      metric_categories                        = optional(set(string), ["AllMetrics"])
      log_analytics_destination_type           = optional(string, "Dedicated")
      workspace_resource_id                    = optional(string, null)
      storage_account_resource_id              = optional(string, null)
      event_hub_authorization_rule_resource_id = optional(string, null)
      event_hub_name                           = optional(string, null)
      marketplace_partner_resource_id          = optional(string, null)
    }))
    default  = {}
    nullable = false
  
    validation {
      condition     = alltrue([for _, v in var.diagnostic_settings : contains(["Dedicated", "AzureDiagnostics"], v.log_analytics_destination_type)])
      error_message = "Log analytics destination type must be one of: 'Dedicated', 'AzureDiagnostics'."
    }
    validation {
      condition = alltrue(
        [
          for _, v in var.diagnostic_settings :
          v.workspace_resource_id != null || v.storage_account_resource_id != null || v.event_hub_authorization_rule_resource_id != null || v.marketplace_partner_resource_id != null
        ]
      )
      error_message = "At least one of `workspace_resource_id`, `storage_account_resource_id`, `marketplace_partner_resource_id`, or `event_hub_authorization_rule_resource_id`, must be set."
    }
    description = <<DESCRIPTION
  A map of diagnostic settings to create on the Key Vault. The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time.
  
  - `name` - (Optional) The name of the diagnostic setting. One will be generated if not set, however this will not be unique if you want to create multiple diagnostic setting resources.
  - `log_categories` - (Optional) A set of log categories to send to the log analytics workspace. Defaults to `[]`.
  - `log_groups` - (Optional) A set of log groups to send to the log analytics workspace. Defaults to `["allLogs"]`.
  - `metric_categories` - (Optional) A set of metric categories to send to the log analytics workspace. Defaults to `["AllMetrics"]`.
  - `log_analytics_destination_type` - (Optional) The destination type for the diagnostic setting. Possible values are `Dedicated` and `AzureDiagnostics`. Defaults to `Dedicated`.
  - `workspace_resource_id` - (Optional) The resource ID of the log analytics workspace to send logs and metrics to.
  - `storage_account_resource_id` - (Optional) The resource ID of the storage account to send logs and metrics to.
  - `event_hub_authorization_rule_resource_id` - (Optional) The resource ID of the event hub authorization rule to send logs and metrics to.
  - `event_hub_name` - (Optional) The name of the event hub. If none is specified, the default event hub will be selected.
  - `marketplace_partner_resource_id` - (Optional) The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic LogsLogs.
  DESCRIPTION
  }
  
  # Sample resource
  resource "azurerm_monitor_diagnostic_setting" "this" {
    for_each                       = var.diagnostic_settings
    name                           = each.value.name != null ? each.value.name : "diag-${var.name}"
    target_resource_id             = azurerm_<MY_RESOURCE>.this.id
    storage_account_id             = each.value.storage_account_resource_id
    eventhub_authorization_rule_id = each.value.event_hub_authorization_rule_resource_id
    eventhub_name                  = each.value.event_hub_name
    partner_solution_id            = each.value.marketplace_partner_resource_id
    log_analytics_workspace_id     = each.value.workspace_resource_id
    log_analytics_destination_type = each.value.log_analytics_destination_type
  
    dynamic "enabled_log" {
      for_each = each.value.log_categories
      content {
        category = enabled_log.value
      }
    }
  
    dynamic "enabled_log" {
      for_each = each.value.log_groups
      content {
        category_group = enabled_log.value
      }
    }
  
    dynamic "enabled_metric" {
      for_each = each.value.metric_categories
      content {
        category = enabled_metric.value
      }
    }
  }
  
  diagnostic_settings = {
    diag_setting_1 = {
      name                                     = "diagSetting1"
      log_groups                               = ["allLogs"]
      metric_categories                        = ["AllMetrics"]
      log_analytics_destination_type           = "Dedicated"
      workspace_resource_id                    = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}"
      storage_account_resource_id              = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{storageAccountName}"
      event_hub_authorization_rule_resource_id = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.EventHub/namespaces/{namespaceName}/eventhubs/{eventHubName}/authorizationrules/{authorizationRuleName}"
      event_hub_name                           = "{eventHubName}"
      marketplace_partner_resource_id          = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{partnerResourceProvider}/{partnerResourceType}/{partnerResourceName}"
    }
  }
  
Note

In the provided example for Diagnostic Settings, both logs and metrics are enabled for the associated resource. However, it is IMPORTANT to note that certain resources may not support both diagnostic setting types/categories. In such cases, the resource configuration MUST be modified accordingly to ensure proper functionality and compliance with system requirements.

Role Assignments

  variable "role_assignments" {
    type = map(object({
      role_definition_id_or_name             = string
      principal_id                           = string
      description                            = optional(string, null)
      skip_service_principal_aad_check       = optional(bool, false)
      condition                              = optional(string, null)
      condition_version                      = optional(string, null)
      delegated_managed_identity_resource_id = optional(string, null)
      principal_type                         = optional(string, null)
    }))
    default     = {}
    nullable    = false
    description = <<DESCRIPTION
  A map of role assignments to create on the <RESOURCE>. The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time.
  
  - `role_definition_id_or_name` - The ID or name of the role definition to assign to the principal.
  - `principal_id` - The ID of the principal to assign the role to.
  - `description` - (Optional) The description of the role assignment.
  - `skip_service_principal_aad_check` - (Optional) If set to true, skips the Azure Active Directory check for the service principal in the tenant. Defaults to false.
  - `condition` - (Optional) The condition which will be used to scope the role assignment.
  - `condition_version` - (Optional) The version of the condition syntax. Leave as `null` if you are not using a condition, if you are then valid values are '2.0'.
  - `delegated_managed_identity_resource_id` - (Optional) The delegated Azure Resource Id which contains a Managed Identity. Changing this forces a new resource to be created. This field is only used in cross-tenant scenario.
  - `principal_type` - (Optional) The type of the `principal_id`. Possible values are `User`, `Group` and `ServicePrincipal`. It is necessary to explicitly set this attribute when creating role assignments if the principal creating the assignment is constrained by ABAC rules that filters on the PrincipalType attribute.
  
  > Note: only set `skip_service_principal_aad_check` to true if you are assigning a role to a service principal.
  DESCRIPTION
  }
  
  locals {
    role_definition_resource_substring = "providers/Microsoft.Authorization/roleDefinitions"
  }
  
  # Example resource declaration
  resource "azurerm_role_assignment" "this" {
    for_each                               = var.role_assignments
    scope                                  = azurerm_MY_RESOURCE.this.id
    role_definition_id                     = strcontains(lower(each.value.role_definition_id_or_name), lower(local.role_definition_resource_substring)) ? each.value.role_definition_id_or_name : null
    role_definition_name                   = strcontains(lower(each.value.role_definition_id_or_name), lower(local.role_definition_resource_substring)) ? null : each.value.role_definition_id_or_name
    principal_id                           = each.value.principal_id
    condition                              = each.value.condition
    condition_version                      = each.value.condition_version
    skip_service_principal_aad_check       = each.value.skip_service_principal_aad_check
    delegated_managed_identity_resource_id = each.value.delegated_managed_identity_resource_id
    principal_type                         = each.value.principal_type
  }
  
  role_assignments = {
    role_assignment_1 = {
      role_definition_id_or_name             = "Contributor"
      principal_id                           = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
      skip_service_principal_aad_check       = true
    },
    role_assignment_2 = {
      role_definition_id_or_name             = "Storage Blob Data Reader"
      principal_id                           = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
      description                            = "Example role assignment 2 of reader role"
      skip_service_principal_aad_check       = false
      condition                              = "@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase 'foo_storage_container'"
      condition_version                      = "2.0"
    }
  }
  

Details on child, extension and cross-referenced resources:

  • Modules MUST support Role Assignments on child, extension and cross-referenced resources as well as the primary resource via parameters/variables

Resource Locks

  variable "lock" {
    type = object({
      kind = string
      name = optional(string, null)
    })
    default     = null
    description = <<DESCRIPTION
  Controls the Resource Lock configuration for this resource. The following properties can be specified:
  
  - `kind` - (Required) The type of lock. Possible values are `\"CanNotDelete\"` and `\"ReadOnly\"`.
  - `name` - (Optional) The name of the lock. If not specified, a name will be generated based on the `kind` value. Changing this forces the creation of a new resource.
  DESCRIPTION
  
    validation {
      condition     = var.lock != null ? contains(["CanNotDelete", "ReadOnly"], var.lock.kind) : true
      error_message = "Lock kind must be either `\"CanNotDelete\"` or `\"ReadOnly\"`."
    }
  }
  
  # Example resource implementation
  resource "azurerm_management_lock" "this" {
    count = var.lock != null ? 1 : 0
  
    lock_level = var.lock.kind
    name       = coalesce(var.lock.name, "lock-${var.lock.kind}")
    scope      = azurerm_MY_RESOURCE.this.id
    notes      = var.lock.kind == "CanNotDelete" ? "Cannot delete the resource or its child resources." : "Cannot delete or modify the resource or its child resources."
  }
  
  lock = {
    name = "lock-{resourcename}" # optional
    type = "CanNotDelete"
  }
  

Details on child and extension resources:

  • Locks SHOULD be able to be set for child resources of the primary resource in resource modules

Details on cross-referenced resources:

  • Locks MUST be automatically applied to cross-referenced resources if the primary resource has a lock applied.
    • This MUST also be able to be turned off for each of the cross-referenced resources by the module consumer via a parameter/variable if they desire

An example of this is a Key Vault module that has a Private Endpoints enabled. If a lock is applied to the Key Vault via the lock parameter/variable then the lock should also be applied to the Private Endpoint automatically, unless the privateEndpointLock/private_endpoint_lock (example name) parameter/variable is set to None

Important

In Terraform, locks become part of the resource graph and suitable depends_on values should be set. Note that, during a destroy operation, Terraform will remove the locks before removing the resource itself, reducing the usefulness of the lock somewhat. Also note, due to eventual consistency in Azure, use of locks can cause destroy operations to fail as the lock may not have been fully removed by the time the destroy operation is executed.

Tags

  variable "tags" {
    type     = map(string)
    default  = null
    description = "(Optional) Tags of the resource."
  }
  
  tags = {
    key           = "value"
    "another-key" = "another-value"
    integers      = 123
  }
  

Details on child, extension and cross-referenced resources:

  • Tags MUST be automatically applied to child, extension and cross-referenced resources, if tags are applied to the primary resource.
    • By default, all tags set for the primary resource will automatically be passed down to child, extension and cross-referenced resources.
    • This MUST be able to be overridden by the module consumer so they can specify alternate tags for child, extension and cross-referenced resources, if they desire via a parameter/variable
      • If overridden by the module consumer, no merge/union of tags will take place from the primary resource and only the tags specified for the child, extension and cross-referenced resources will be applied

Managed Identities

  variable "managed_identities" {
    type = object({
      system_assigned            = optional(bool, false)
      user_assigned_resource_ids = optional(set(string), [])
    })
    default     = {}
    nullable    = false
    description = <<DESCRIPTION
  Controls the Managed Identity configuration on this resource. The following properties can be specified:
  
  - `system_assigned` - (Optional) Specifies if the System Assigned Managed Identity should be enabled.
  - `user_assigned_resource_ids` - (Optional) Specifies a list of User Assigned Managed Identity resource IDs to be assigned to this resource.
  DESCRIPTION
  }
  
  # Helper locals to make the dynamic block more readable
  # There are three attributes here to cater for resources that
  # support both user and system MIs, only system MIs, and only user MIs
  locals {
    managed_identities = {
      system_assigned_user_assigned = (var.managed_identities.system_assigned || length(var.managed_identities.user_assigned_resource_ids) > 0) ? {
        this = {
          type                       = var.managed_identities.system_assigned && length(var.managed_identities.user_assigned_resource_ids) > 0 ? "SystemAssigned, UserAssigned" : length(var.managed_identities.user_assigned_resource_ids) > 0 ? "UserAssigned" : "SystemAssigned"
          user_assigned_resource_ids = var.managed_identities.user_assigned_resource_ids
        }
      } : {}
      system_assigned = var.managed_identities.system_assigned ? {
        this = {
          type = "SystemAssigned"
        }
      } : {}
      user_assigned = length(var.managed_identities.user_assigned_resource_ids) > 0 ? {
        this = {
          type                       = "UserAssigned"
          user_assigned_resource_ids = var.managed_identities.user_assigned_resource_ids
        }
      } : {}
    }
  }
  
  ## Resources supporting both SystemAssigned and UserAssigned
  dynamic "identity" {
    for_each = local.managed_identities.system_assigned_user_assigned
    content {
      type         = identity.value.type
      identity_ids = identity.value.user_assigned_resource_ids
    }
  }
  
  ## Resources that only support SystemAssigned
  dynamic "identity" {
    for_each = identity.managed_identities.system_assigned
    content {
      type = identity.value.type
    }
  }
  
  ## Resources that only support UserAssigned
  dynamic "identity" {
    for_each = local.managed_identities.user_assigned
    content {
      type         = identity.value.type
      identity_ids = identity.value.user_assigned_resource_ids
    }
  }
  
  managed_identities = {
    system_assigned = true
    user_assigned_resource_ids = [
      "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}",
      "/subscriptions/{subscriptionId2}/resourceGroups/{resourceGroupName2}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName2}"
    ]
  }
  

Reason for differences in User Assigned data type in languages:

  • We do not forsee the Managed Identity Resource Provider team to ever add additional properties within the empty object ({}) value required on the input of a User Assigned Managed Identity.
  • In Bicep we therefore have removed the need for this to be declared and just converted it to a simple array of Resource IDs
  • However, in Terraform we have left it as a object/map as this simplifies for_each and other loop mechanisms and provides more consistency in plan, apply, destroy operations
    • Especially when adding, removing or changing the order of the User Assigned Managed Identities as they are declared

Private Endpoints

  # In this example we only support one service, e.g. Key Vault.
  # If your service has multiple private endpoint services, then expose the service name.
  
  # This variable is used to determine if the private_dns_zone_group block should be included,
  # or if it is to be managed externally, e.g. using Azure Policy.
  # https://github.com/Azure/terraform-azurerm-avm-res-keyvault-vault/issues/32
  # Alternatively you can use AzAPI, which does not have this issue.
  variable "private_endpoints_manage_dns_zone_group" {
    type        = bool
    default     = true
    nullable    = false
    description = "Whether to manage private DNS zone groups with this module. If set to false, you must manage private DNS zone groups externally, e.g. using Azure Policy."
  }
  
  variable "private_endpoints" {
    type = map(object({
      name               = optional(string, null)
      role_assignments   = optional(map(object({
        role_definition_id_or_name             = string
        principal_id                           = string
        description                            = optional(string, null)
        skip_service_principal_aad_check       = optional(bool, false)
        condition                              = optional(string, null)
        condition_version                      = optional(string, null)
        delegated_managed_identity_resource_id = optional(string, null)
        principal_type         							   = optional(string, null)
      })), {})
      lock               = optional(object({
        kind = string
        name = optional(string, null)
      }), null)
      tags               = optional(map(string), null)
      subnet_resource_id = string
      subresource_name   = string  # NOTE: `subresource_name` can be excluded if the resource does not support multiple sub resource types (e.g. storage account supports blob, queue, etc)
      private_dns_zone_group_name             = optional(string, "default")
      private_dns_zone_resource_ids           = optional(set(string), [])
      application_security_group_associations = optional(map(string), {})
      private_service_connection_name         = optional(string, null)
      network_interface_name                  = optional(string, null)
      location                                = optional(string, null)
      resource_group_name                     = optional(string, null)
      ip_configurations = optional(map(object({
        name               = string
        private_ip_address = string
      })), {})
    }))
    default     = {}
    nullable    = false
    description = <<DESCRIPTION
  A map of private endpoints to create on the Key Vault. The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time.
  
  - `name` - (Optional) The name of the private endpoint. One will be generated if not set.
  - `role_assignments` - (Optional) A map of role assignments to create on the private endpoint. The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time. See `var.role_assignments` for more information.
    - `role_definition_id_or_name` - The ID or name of the role definition to assign to the principal.
    - `principal_id` - The ID of the principal to assign the role to.
    - `description` - (Optional) The description of the role assignment.
    - `skip_service_principal_aad_check` - (Optional) If set to true, skips the Azure Active Directory check for the service principal in the tenant. Defaults to false.
    - `condition` - (Optional) The condition which will be used to scope the role assignment.
    - `condition_version` - (Optional) The version of the condition syntax. Leave as `null` if you are not using a condition, if you are then valid values are '2.0'.
    - `delegated_managed_identity_resource_id` - (Optional) The delegated Azure Resource Id which contains a Managed Identity. Changing this forces a new resource to be created. This field is only used in cross-tenant scenario.
    - `principal_type` - (Optional) The type of the `principal_id`. Possible values are `User`, `Group` and `ServicePrincipal`. It is necessary to explicitly set this attribute when creating role assignments if the principal creating the assignment is constrained by ABAC rules that filters on the PrincipalType attribute.
  - `lock` - (Optional) The lock level to apply to the private endpoint. Default is `None`. Possible values are `None`, `CanNotDelete`, and `ReadOnly`.
    - `kind` - (Required) The type of lock. Possible values are `\"CanNotDelete\"` and `\"ReadOnly\"`.
    - `name` - (Optional) The name of the lock. If not specified, a name will be generated based on the `kind` value. Changing this forces the creation of a new resource.
  - `tags` - (Optional) A mapping of tags to assign to the private endpoint.
  - `subnet_resource_id` - The resource ID of the subnet to deploy the private endpoint in.
  - `subresource_name` - The name of the sub resource for the private endpoint.
  - `private_dns_zone_group_name` - (Optional) The name of the private DNS zone group. One will be generated if not set.
  - `private_dns_zone_resource_ids` - (Optional) A set of resource IDs of private DNS zones to associate with the private endpoint. If not set, no zone groups will be created and the private endpoint will not be associated with any private DNS zones. DNS records must be managed external to this module.
  - `application_security_group_resource_ids` - (Optional) A map of resource IDs of application security groups to associate with the private endpoint. The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time.
  - `private_service_connection_name` - (Optional) The name of the private service connection. One will be generated if not set.
  - `network_interface_name` - (Optional) The name of the network interface. One will be generated if not set.
  - `location` - (Optional) The Azure location where the resources will be deployed. Defaults to the location of the resource group.
  - `resource_group_name` - (Optional) The resource group where the resources will be deployed. Defaults to the resource group of the Key Vault.
  - `ip_configurations` - (Optional) A map of IP configurations to create on the private endpoint. If not specified the platform will create one. The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time.
    - `name` - The name of the IP configuration.
    - `private_ip_address` - The private IP address of the IP configuration.
  DESCRIPTION
  }
  
  # The PE resource when we are managing the private_dns_zone_group block:
  resource "azurerm_private_endpoint" "this" {
    for_each                      = { for k, v in var.private_endpoints : k => v if var.private_endpoints_manage_dns_zone_group }
    name                          = each.value.name != null ? each.value.name : "pep-${var.name}"
    location                      = each.value.location != null ? each.value.location : var.location
    resource_group_name           = each.value.resource_group_name != null ? each.value.resource_group_name : var.resource_group_name
    subnet_id                     = each.value.subnet_resource_id
    custom_network_interface_name = each.value.network_interface_name
    tags                          = each.value.tags
  
    private_service_connection {
      name                           = each.value.private_service_connection_name != null ? each.value.private_service_connection_name : "pse-${var.name}"
      private_connection_resource_id = azurerm_key_vault.this.id
      is_manual_connection           = false
      subresource_names              = ["MYSERVICE"] # map to each.value.subresource_name if there are multiple services.
    }
  
    dynamic "private_dns_zone_group" {
      for_each = length(each.value.private_dns_zone_resource_ids) > 0 ? ["this"] : []
  
      content {
        name                 = each.value.private_dns_zone_group_name
        private_dns_zone_ids = each.value.private_dns_zone_resource_ids
      }
    }
  
    dynamic "ip_configuration" {
      for_each = each.value.ip_configurations
  
      content {
        name               = ip_configuration.value.name
        subresource_name   = "MYSERVICE" # map to each.value.subresource_name if there are multiple services.
        member_name        = "MYSERVICE" # map to each.value.subresource_name if there are multiple services.
        private_ip_address = ip_configuration.value.private_ip_address
      }
    }
  }
  
  # The PE resource when we are managing **not** the private_dns_zone_group block:
  resource "azurerm_private_endpoint" "this_unmanaged_dns_zone_groups" {
    for_each = { for k, v in var.private_endpoints : k => v if !var.private_endpoints_manage_dns_zone_group }
  
    # ... repeat configuration above
    # **omitting the private_dns_zone_group block**
    # then add the following lifecycle block to ignore changes to the private_dns_zone_group block
  
    lifecycle {
      ignore_changes = [private_dns_zone_group]
    }
  }
  
  # Private endpoint application security group associations.
  # We merge the nested maps from private endpoints and application security group associations into a single map.
  locals {
    private_endpoint_application_security_group_associations = { for assoc in flatten([
      for pe_k, pe_v in var.private_endpoints : [
        for asg_k, asg_v in pe_v.application_security_group_associations : {
          asg_key         = asg_k
          pe_key          = pe_k
          asg_resource_id = asg_v
        }
      ]
    ]) : "${assoc.pe_key}-${assoc.asg_key}" => assoc }
  }
  
  resource "azurerm_private_endpoint_application_security_group_association" "this" {
    for_each                      = local.private_endpoint_application_security_group_associations
    private_endpoint_id           = azurerm_private_endpoint.this[each.value.pe_key].id
    application_security_group_id = each.value.asg_resource_id
  }
  
  # You need an additional resource when not managing private_dns_zone_group with this module:
  
  # In your output you need to select the correct resource based on the value of var.private_endpoints_manage_dns_zone_group:
  output "private_endpoints" {
    value       = var.private_endpoints_manage_dns_zone_group ? azurerm_private_endpoint.this : azurerm_private_endpoint.this_unmanaged_dns_zone_groups
    description = <<DESCRIPTION
  A map of the private endpoints created.
  DESCRIPTION
  }
  
  private_endpoints = {
    pe1 = {
      role_assignments   = {} # see interfaces/role assignments
      lock               = {} # see interfaces/resource locks
      tags               = {} # see interfaces/tags
      subnet_resource_id = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{vnetName}/subnets/{subnetName}"
      private_dns_zone_resource_ids = [
        "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/privateDnsZones/{dnsZoneName}"
      ]
      application_security_group_associations = {
        asg1 = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/applicationSecurityGroups/{asgName}"
      }
      network_interface_name = "nic1"
      ip_configurations = {
        ipconfig1 = {
          name               = "ipconfig1"
          group_id           = "vault"
          member_name        = "default"
          private_ip_address = "10.0.0.7"
        }
      }
    }
  }
  

Notes:

  • The properties defined in the schema above are the minimum amount of properties expected to be exposed for Private Endpoints in AVM Resource Modules.
    • A module owner MAY chose to expose additional properties of the Private Endpoint resource.
      • However, module owners considering this SHOULD contact the AVM core team first to consult on how the property should be exposed to avoid future breaking changes to the schema that may be enforced upon them.
  • Module owners MAY chose to define a list of allowed value for the ‘service’ (a.k.a. groupIds) property.
    • However, they should do so with caution as should a new service appear for their resource module, a new release will need to be cut to add this new service to the allowed values.
      • Whereas not specifying allowed values will allow flexibility from day 0 without the need for any changes and releases to be made.

Customer Managed Keys

  variable "customer_managed_key" {
    type = object({
      key_vault_resource_id  = string
      key_name               = string
      key_version            = optional(string, null)
      user_assigned_identity = optional(object({
        resource_id = string
      }), null)
    })
    default = null
  }
  
  customer_managed_key = {
    key_vault_resource_id: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{keyVaultName}"
    key_name: "{keyName}"
    key_version: "{keyVersion}"
    user_assigned_identity_resource_id: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{uamiName}"
  }
  

Azure Monitor Alerts

Note

This interface is a SHOULD instead of a MUST and therefore the AVM core team have not mandated a interface schema to use.

Terraform Pattern Module Specifications

Contribution / Support

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR8Module Owner(s) GitHubMUSTOwnerInitial
2SNFR20GitHub Teams OnlyMUSTOwnerInitial
3SNFR9AVM & PG Teams GitHub Repo PermissionsMUSTOwnerInitial
4SNFR10MIT LicensingMUSTOwnerInitial
5SNFR11Issues Response TimesMUSTOwnerContributorBAU
6SNFR12Versions SupportedMUSTOwnerBAU
7SNFR23GitHub Repo LabelsMUSTOwnerBAU
8TFNFR3GitHub Repo Branch ProtectionMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.

Note

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.




See origin...

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 a GitHub team assigned for module owners. This team MUST be created in the Azure organization in GitHub.

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

Info

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.

Bicep

Important

As part of the module proposal process, the name of the GitHub team for each approved module is already defined in the respective Module Indexes (or CSV file). This team MUST be created (and used) for each module.

Module owners don’t need to construct the name of the GitHub team for their module themselves, instead they need use the name prescribed in the related CSV file, at the time of approval.

For a direct link, see the list of related index pages:

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!

Naming Convention

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

  • <hyphenated module name>-module-owners-bicep - to grant permissions for module owners on Bicep modules

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 == the role the GitHub Team is assigned to
  • <bicep == the language the module is written in

Examples:

  • avm-res-compute-virtualmachine-module-owners-bicep
Note

The naming convention for Bicep modules is slightly different than the naming convention for their respective GitHub teams.

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.

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

Grant permissions through team memberships

Note

In case of Bicep modules, permissions to the BRM repository (the repo of the Bicep Registry) are granted via assigning the -module-owners- 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 team to the respective parent, only the AVM core team can approve this parent-child relationship.

Module owners MUST create their -module-owners- team and as part of the provisioning process, they MUST request the addition of this team to its respective parent team (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.

Example - GitHub team 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.
Tip

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).

Note

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 Bicep reviewers, with the @Azure/ prefix, i.e., @Azure/avm-module-reviewers-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-module-reviewers-bicep

Terraform

Note

Access management for Terraform repositories now uses a single team, membership of which is managed using an internal entitlement management tool (Core Identity).

All module owners MUST request access to the avm-module-owners-terraform GitHub team via the Azure Verified Module Owners Terraform entitlement in Core Identity (Microsoft internal tool).




See origin...

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

Note

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

Terraform

Important

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.




See origin...

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

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




See origin...

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

A module owner MUST respond to logged issues as defined in the support statement. See Module Support for more information.




See origin...

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.




See origin...

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:

➕ AVM Standard GitHub 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 📣In the AVM repository: this module needs an owner to develop or maintain it. In the BRM repository: the module owner needs to review a PR.
FF0019
Needs: Module Contributor 📣This module needs secondary owner(s) or contributor(s) to develop or maintain it
C95474
Needs: Core Team 🧞‍♂️This item needs the AVM Core Team to review it
DB4503
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 Deprecated 🔴This is a request to deprecate a module
000000
Status: Module Orphaned 🟡The module has no owner and is therefore orphaned at this time
F4A460
Status: Ready For Repository Creation 📝This module is approved and the owner is ready for the repository to be created (Terraform)
136A41
Status: Repository Created 📄This module has had it's repository created and configured ready for owner contribution (Terraform)
27AB03
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: CI 🚀This issue is related to the AVM CI
74CFB0
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
Class: Utility Module 📦This is a utility module
CAD1DE
Class: Child Module 📦This is a child module
5E5186

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

➕ Set-AvmGitHubLabels.ps1

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
```shell
# 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
  



See origin...

ID: TFNFR3 - Category: Contribution/Support - GitHub Repo Branch Protection

Module owners MUST set a branch protection policy on their GitHub Repositories for AVM modules against their default branch, typically main, to do the following:

  1. Requires a Pull Request before merging
  2. Require approval of the most recent reviewable push
  3. Dismiss stale pull request approvals when new commits are pushed
  4. Require linear history
  5. Prevents force pushes
  6. Not allow deletions
  7. Require CODEOWNERS review
  8. Do not allow bypassing the above settings
  9. Above settings MUST also be enforced to administrators
Tip

If you use the template repository as mentioned in the contribution guide, the above will automatically be set.




Telemetry

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR3Deployment/Usage TelemetryMUSTOwnerInitial
2SFR4Telemetry Enablement FlexibilityMUSTOwnerInitial
➕ See Specifications for this category
See origin...

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

Modules MUST provide the capability to collect deployment/usage telemetry as detailed in Telemetry further.

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.)

Telemetry Information Notice

Note

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

  • Bicep: Using the 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.

Module Class Applicability

This specification applies to all AVM module classes (resource, pattern, utility), however, in case of utility modules, telemetry collection MUST only be added when the utility module deploys any resources (e.g., a deployment script resource). If the utility module does not deploy any resources, telemetry collection MUST NOT be added.

Bicep

Important

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.

The value you need to use for your module is defined in the related module index. You can look it up on the index pages for Resource Modules, Pattern Modules and Utility Modules.

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

  • <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-
    • The prefixes: avm-ptn-
  • <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)
Note

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.

An example deployment name for the AVM Virtual Machine Resource Module would be: 46d3xbcp.res.compute-virtualmachine.1-2-3.eum3

An example deployment name for a shortened module name would be: 46d3xbcp.res.desktopvirtualization-appgroup.1-2-3.eum3

Tip

Terraform: Terraform uses a telemetry provider, the configuration of which is the same for every module and is included in the template repo.

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

Terraform

To enable telemetry data collection for Terraform modules, the modtm telemetry provider MUST be used. 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.

The modtm provider MUST be 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"
    }
  }
}



See origin...

ID: SFR4 - Category: Telemetry - Telemetry Enablement Flexibility

The telemetry collection MUST be on/enabled by default, however module consumers MUST be allowed to disable it by setting the below parameter/variable value to false:

  • Bicep: enableTelemetry
  • Terraform: enable_telemetry
Note

Whenever a module references AVM modules that implement the telemetry parameter (e.g., a pattern module that uses AVM resource modules), the telemetry parameter value MUST be passed through to these modules. This is necessary to ensure a consumer can reliably enable & disable the telemetry feature for all used modules.

This general specification can be modified for some use-cases, that are language specific:

Bicep

For cross-references in resource modules, the spec BCPFR7 also applies.

Terraform

Currently, no further requirements apply.




Naming / Composition

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR1Preview ServicesMUSTOwnerBAU
2SFR2WAF AlignedSHOULDOwnerBAU
3SFR5Availability ZonesMUSTOwnerInitial
4SFR6Data RedundancyMUSTOwnerInitial
5SNFR25Resource NamingMUSTOwnerInitial
6PMFR1Resource Group CreationMAYOwnerContributorBAU
7PMNFR1Module NamingMUSTOwnerInitial
8PMNFR2Use Resource Modules to Build a Pattern ModuleMUSTOwnerContributorBAU
9PMNFR3Use other Pattern Modules to Build a Pattern ModuleMUSTOwnerContributorBAU
10TFFR1Cross-Referencing ModulesMUSTOwnerContributorBAU
11TFFR3Providers - Permitted VersionsMUSTOwnerContributorBAU
12TFNFR4Lower snake_casingMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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”



See origin...

ID: SFR2 - Category: Composition - WAF Aligned

Modules SHOULD set defaults in input parameters/variables to align to high priority/impact/severity 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.

Tip

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




See origin...

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.

Note

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




See origin...

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.

Note

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




See origin...

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>.

Tip

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.




See origin...

ID: PMFR1 - Category: Composition - Resource Group Creation

A Pattern Module MAY create Resource Group(s).




See origin...

ID: PMNFR1 - Category: Naming - Module Naming

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

Important

As part of the module proposal process, the module’s approved name is captured both in the module proposal issue AND the related module index page (backed by the corresponding CSV file).

Therefore, module owners don’t need to construct the module’s name themselves, instead they need use the name prescribed in the module proposal issue or in the related CSV file, at the time of approval.

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



See origin...

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.
Note

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.




See origin...

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.




See origin...

ID: TFFR1 - Category: Composition - Cross-Referencing Modules

Module owners MAY cross-references other modules to build either Resource or Pattern modules. However, they MUST be referenced only by a HashiCorp Terraform registry reference to a pinned version e.g.,

module "other-module" {
  source  = "Azure/xxx/azurerm"
  version = "1.2.3"
}

They MUST NOT use git reference to a module.

module "other-module" {
  source = "git::https://xxx.yyy/xxx.git"
}
module "other-module" {
  source = "github.com/xxx/yyy"
}

Modules MUST NOT contain references to non-AVM modules.

Tip

See Module Sources for more information.




See origin...

ID: TFFR3 - Category: Providers - Permitted Versions

Authors MUST only use the following Azure providers, and versions, in their modules:

providermin versionmax version
azapi>= 2.0< 3.0
azurerm>= 4.0< 5.0
Note

Authors MAY select either Azurerm, Azapi, or both providers in their module.

Authors MUST use the required_providers block in their module to enforce the provider versions.

The following is an example.

terraform {
  required_providers {
    # Include one or both providers, as needed
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
    azapi = {
      source  = "Azure/azapi"
      version = "~> 2.0"
    }
  }
}



See origin...

ID: TFNFR4 - Category: Composition - Code Styling - lower snake_casing

Module owners MUST use lower snake_casing for naming the following:

  • Locals
  • Variables
  • Outputs
  • Resources (symbolic names)
  • Modules (symbolic names)

For example: snake_casing_example (every word in lowercase, with each word separated by an underscore _)




Code Style

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1TFNFR6Resource & Data OrderSHOULDOwnerContributorBAU
2TFNFR7Count & for_each UseMUSTOwnerContributorBAU
3TFNFR8Resource & Data Block OrdersSHOULDOwnerContributorBAU
4TFNFR9Module Block OrderSHOULDOwnerContributorBAU
5TFNFR10No Double Quotes in ignore_changesMUSTOwnerContributorBAU
6TFNFR11Null Comparison ToggleSHOULDOwnerContributorBAU
7TFNFR12Dynamic for Optional Nested ObjectsMUSTOwnerContributorBAU
8TFNFR13Default Values with coalesce/trySHOULDOwnerContributorBAU
9TFNFR16Variable Naming RulesSHOULDOwnerContributorBAU
10TFNFR17Variables with DescriptionsSHOULDOwnerContributorBAU
11TFNFR18Variables with TypesMUSTOwnerContributorBAU
12TFNFR19Sensitive Data VariablesSHOULDOwnerContributorBAU
13TFNFR20Non-Nullable Defaults for collection valuesSHOULDOwnerContributorBAU
14TFNFR21Discourage Nullability by DefaultMUSTOwnerContributorBAU
15TFNFR22Avoid sensitive = falseMUSTOwnerContributorBAU
16TFNFR23Sensitive Default Value ConditionsMUSTOwnerContributorBAU
17TFNFR24Handling Deprecated VariablesMUSTOwnerContributorBAU
18TFNFR25Verified Modules RequirementsMUSTOwnerContributorBAU
19TFNFR26Providers in required_providersMUSTOwnerContributorBAU
20TFNFR27Provider Declarations in ModulesMUSTOwnerContributorBAU
22TFNFR30Handling Deprecated OutputsMUSTOwnerContributorBAU
23TFNFR31locals.tf for Locals OnlyMAYOwnerContributorBAU
25TFNFR33Precise Local TypesSHOULDOwnerContributorBAU
26TFNFR34Using Feature TogglesMUSTOwnerContributorBAU
27TFNFR35Reviewing Potential Breaking ChangesMUSTOwnerContributorBAU
28TFNFR36Setting prevent_deletion_if_contains_resourcesSHOULDOwnerContributorBAU
29TFNFR37Tool Usage by Module OwnerMAYOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: TFNFR6 - Category: Code Style - Resource & Data Order

For the definition of resources in the same file, the resources be depended on SHOULD come first, after them are the resources depending on others.

Resources that have dependencies SHOULD be defined close to each other.




See origin...

ID: TFNFR7 - Category: Code Style - count & for_each Use

We can use count and for_each to deploy multiple resources, but the improper use of count can lead to anti pattern.

You can use count to create some kind of resources under certain conditions, for example:

resource "azurerm_network_security_group" "this" {
  count               = local.create_new_security_group ? 1 : 0
  name                = coalesce(var.new_network_security_group_name, "${var.subnet_name}-nsg")
  resource_group_name = var.resource_group_name
  location            = local.location
  tags                = var.new_network_security_group_tags
}

The module’s owners MUST use map(xxx) or set(xxx) as resource’s for_each collection, the map’s key or set’s element MUST be static literals.

Good example:

resource "azurerm_subnet" "pair" {
  for_each             = var.subnet_map // `map(string)`, when user call this module, it could be: `{ "subnet0": "subnet0" }`, or `{ "subnet0": azurerm_subnet.subnet0.name }`
  name                 = "${each.value}"-pair
  resource_group_name  = azurerm_resource_group.example.name
  virtual_network_name = azurerm_virtual_network.example.name
  address_prefixes     = ["10.0.1.0/24"]
}

Bad example:

resource "azurerm_subnet" "pair" {
  for_each             = var.subnet_name_set // `set(string)`, when user use `toset([azurerm_subnet.subnet0.name])`, it would cause an error.
  name                 = "${each.value}"-pair
  resource_group_name  = azurerm_resource_group.example.name
  virtual_network_name = azurerm_virtual_network.example.name
  address_prefixes     = ["10.0.1.0/24"]
}



See origin...

ID: TFNFR8 - Category: Code Style - Resource & Data Block Orders

There are 3 types of assignment statements in a resource or data block: argument, meta-argument and nested block. The argument assignment statement is a parameter followed by =:

location = azurerm_resource_group.example.location

or:

tags = {
  environment = "Production"
}

Nested block is a assignment statement of parameter followed by {} block:

subnet {
  name           = "subnet1"
  address_prefix = "10.0.1.0/24"
}

Meta-arguments are assignment statements can be declared by all resource or data blocks. They are:

  • count
  • depends_on
  • for_each
  • lifecycle
  • provider

The order of declarations within resource or data blocks is:

All the meta-arguments SHOULD be declared on the top of resource or data blocks in the following order:

  1. provider
  2. count
  3. for_each

Then followed by:

  1. required arguments
  2. optional arguments
  3. required nested blocks
  4. optional nested blocks

All ranked in alphabetical order.

These meta-arguments SHOULD be declared at the bottom of a resource block with the following order:

  1. depends_on
  2. lifecycle

The parameters of lifecycle block SHOULD show up in the following order:

  1. create_before_destroy
  2. ignore_changes
  3. prevent_destroy

parameters under depends_on and ignore_changes are ranked in alphabetical order.

Meta-arguments, arguments and nested blocked are separated by blank lines.

dynamic nested blocks are ranked by the name comes after dynamic, for example:

  dynamic "linux_profile" {
    for_each = var.admin_username == null ? [] : ["linux_profile"]

    content {
      admin_username = var.admin_username

      ssh_key {
        key_data = replace(coalesce(var.public_ssh_key, tls_private_key.ssh[0].public_key_openssh), "\n", "")
      }
    }
  }

This dynamic block will be ranked as a block named linux_profile.

Code within a nested block will also be ranked following the rules above.

PS: You can use avmfix tool to reformat your code automatically.




See origin...

ID: TFNFR9 - Category: Code Style - Module Block Order

The meta-arguments below SHOULD be declared on the top of a module block with the following order:

  1. source
  2. version
  3. count
  4. for_each

blank lines will be used to separate them.

After them will be required arguments, optional arguments, all ranked in alphabetical order.

These meta-arguments below SHOULD be declared on the bottom of a resource block in the following order:

  1. depends_on
  2. providers

Arguments and meta-arguments SHOULD be separated by blank lines.




See origin...

ID: TFNFR10 - Category: Code Style - No Double Quotes in ignore_changes

The ignore_changes attribute MUST NOT be enclosed in double quotes.

Good example:

lifecycle {
    ignore_changes = [
      tags,
    ]
}

Bad example:

lifecycle {
    ignore_changes = [
      "tags",
    ]
}



See origin...

ID: TFNFR11 - Category: Code Style - Null Comparison Toggle

Sometimes we need to ensure that the resources created are compliant to some rules at a minimum extent, for example a subnet has to be connected to at least one network_security_group. The user SHOULD pass in a security_group_id and ask us to make a connection to an existing security_group, or want us to create a new security group.

Intuitively, we will define it like this:

variable "security_group_id" {
  type: string
}

resource "azurerm_network_security_group" "this" {
  count               = var.security_group_id == null ? 1 : 0
  name                = coalesce(var.new_network_security_group_name, "${var.subnet_name}-nsg")
  resource_group_name = var.resource_group_name
  location            = local.location
  tags                = var.new_network_security_group_tags
}

The disadvantage of this approach is if the user create a security group directly in the root module and use the id as a variable of the module, the expression which determines the value of count will contain an attribute from another resource, the value of this very attribute is “known after apply” at plan stage. Terraform core will not be able to get an exact plan of deployment during the “plan” stage.

You can’t do this:

resource "azurerm_network_security_group" "foo" {
  name                = "example-nsg"
  resource_group_name = "example-rg"
  location            = "eastus"
}

module "bar" {
  source = "xxxx"
  ...
  security_group_id = azurerm_network_security_group.foo.id
}

For this kind of parameters, wrapping with object type is RECOMMENDED:

variable "security_group" {
  type: object({
    id   = string
  })
  default     = null
}

The advantage of doing so is encapsulating the value which is “known after apply” in an object, and the object itself can be easily found out if it’s null or not. Since the id of a resource cannot be null, this approach can avoid the situation we are facing in the first example, like the following:

resource "azurerm_network_security_group" "foo" {
  name                = "example-nsg"
  resource_group_name = "example-rg"
  location            = "eastus"
}

module "bar" {
  source = "xxxx"
  ...
  security_group = {
    id = azurerm_network_security_group.foo.id
  }
}

This technique SHOULD be used under this use case only.




See origin...

ID: TFNFR12 - Category: Code Style - Dynamic for Optional Nested Objects

An example from the community:

resource "azurerm_kubernetes_cluster" "main" {
  ...
  dynamic "identity" {
    for_each = var.client_id == "" || var.client_secret == "" ? [1] : []

    content {
      type                      = var.identity_type
      user_assigned_identity_id = var.user_assigned_identity_id
    }
  }
  ...
}

Please refer to the coding style in the example. Nested blocks under conditions, MUST be declared as:

for_each = <condition> ? [<some_item>] : []



See origin...

ID: TFNFR13 - Category: Code Style - Default Values with coalesce/try

The following example shows how "${var.subnet_name}-nsg" SHOULD be used when var.new_network_security_group_name is null or ""

Good examples:

coalesce(var.new_network_security_group_name, "${var.subnet_name}-nsg")
try(coalesce(var.new_network_security_group.name, "${var.subnet_name}-nsg"), "${var.subnet_name}-nsg")

Bad examples:

var.new_network_security_group_name == null ? "${var.subnet_name}-nsg" : var.new_network_security_group_name)



See origin...

ID: TFNFR16 - Category: Code Style - Variable Naming Rules

The naming of a variable SHOULD follow HashiCorp’s naming rule.

variable used as feature switches SHOULD apply a positive statement, use xxx_enabled instead of xxx_disabled. Avoid double negatives like !xxx_disabled.

Please use xxx_enabled instead of xxx_disabled as name of a variable.




See origin...

ID: TFNFR17 - Category: Code Style - Variables with Descriptions

The target audience of description is the module users.

For a newly created variable (Eg. variable for switching dynamic block on-off), it’s description SHOULD precisely describe the input parameter’s purpose and the expected data type. description SHOULD NOT contain any information for module developers, this kind of information can only exist in code comments.

For object type variable, description can be composed in HEREDOC format:

variable "kubernetes_cluster_key_management_service" {
  type: object({
    key_vault_key_id         = string
    key_vault_network_access = optional(string)
  })
  default     = null
  description = <<-EOT
  - `key_vault_key_id` - (Required) Identifier of Azure Key Vault key. See [key identifier format](https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#vault-name-and-object-name) for more details. When Azure Key Vault key management service is enabled, this field is required and must be a valid key identifier. When `enabled` is `false`, leave the field empty.
  - `key_vault_network_access` - (Optional) Network access of the key vault Network access of key vault. The possible values are `Public` and `Private`. `Public` means the key vault allows public access from all networks. `Private` means the key vault disables public access and enables private link. Defaults to `Public`.
EOT
}



See origin...

ID: TFNFR18 - Category: Code Style - Variables with Types

type MUST be defined for every variable. type SHOULD be as precise as possible, any MAY only be defined with adequate reasons.

  • Use bool instead of string or number for true/false
  • Use string for text
  • Use concrete object instead of map(any)



See origin...

ID: TFNFR19 - Category: Code Style - Sensitive Data Variables

If variable’s type is object and contains one or more fields that would be assigned to a sensitive argument, then this whole variable SHOULD be declared as sensitive = true, otherwise you SHOULD extract sensitive field into separated variable block with sensitive = true.




See origin...

ID: TFNFR20 - Category: Code Style - Non-Nullable Defaults for collection values

Nullable SHOULD be set to false for collection values (e.g. sets, maps, lists) when using them in loops. However for scalar values like string and number, a null value MAY have a semantic meaning and as such these values are allowed.




See origin...

ID: TFNFR21 - Category: Code Style - Discourage Nullability by Default

nullable = true MUST be avoided.




See origin...

ID: TFNFR22 - Category: Code Style - Avoid sensitive = false

sensitive = false MUST be avoided.




See origin...

ID: TFNFR23 - Category: Code Style - Sensitive Default Value Conditions

A default value MUST NOT be set for a sensitive input - e.g., a default password.




See origin...

ID: TFNFR24 - Category: Code Style - Handling Deprecated Variables

Sometimes we will find names for some variable are not suitable anymore, or a change SHOULD be made to the data type. We want to ensure forward compatibility within a major version, so direct changes are strictly forbidden. The right way to do this is move this variable to an independent deprecated_variables.tf file, then redefine the new parameter in variable.tf and make sure it’s compatible everywhere else.

Deprecated variable MUST be annotated as DEPRECATED at the beginning of the description, at the same time the replacement’s name SHOULD be declared. E.g.,

variable "enable_network_security_group" {
  type        = string
  default     = null
  description = "DEPRECATED, use `network_security_group_enabled` instead; Whether to generate a network security group and assign it to the subnet. Changing this forces a new resource to be created."
}

A cleanup of deprecated_variables.tf SHOULD be performed during a major version release.




See origin...

ID: TFNFR25 - Category: Code Style - Verified Modules Requirements

The terraform.tf file MUST only contain one terraform block.

The first line of the terraform block MUST define a required_version property for the Terraform CLI.

The required_version property MUST include a constraint on the minimum version of the Terraform CLI. Previous releases of the Terraform CLI can have unexpected behavior.

The required_version property MUST include a constraint on the maximum major version of the Terraform CLI. Major version releases of the Terraform CLI can introduce breaking changes and MUST be tested.

The required_version property constraint SHOULD use the ~> #.# or the >= #.#.#, < #.#.# format.

Note: You can read more about Terraform version constraints in the documentation.

Example terraform.tf file:

terraform {
  required_version = "~> 1.6"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.11"
    }
  }
}



See origin...

ID: TFNFR26 - Category: Code Style - Providers in required_providers

The terraform block in terraform.tf MUST contain the required_providers block.

Each provider used directly in the module MUST be specified with the source and version properties. Providers in the required_providers block SHOULD be sorted in alphabetical order.

Do not add providers to the required_providers block that are not directly required by this module. If submodules are used then each submodule SHOULD have its own versions.tf file.

The source property MUST be in the format of namespace/name. If this is not explicitly specified, it can cause failure.

The version property MUST include a constraint on the minimum version of the provider. Older provider versions may not work as expected.

The version property MUST include a constraint on the maximum major version. A provider major version release may introduce breaking change, so updates to the major version constraint for a provider MUST be tested.

The version property constraint SHOULD use the ~> #.# or the >= #.#.#, < #.#.# format.

Note: You can read more about Terraform version constraints in the documentation.

Good examples:

terraform {
  required_version = "~> 1.6"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }
}
terraform {
  required_version = ">= 1.6.6, < 2.0.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.11.1, < 4.0.0"
    }
  }
}
terraform {
  required_version = ">= 1.6, < 2.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.11, < 4.0"
    }
  }
}

Acceptable example (but not recommended):

terraform {
  required_version = "1.6"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "3.11"
    }
  }
}

Bad example:

terraform {
  required_version = ">= 1.6"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.11"
    }
  }
}



See origin...

ID: TFNFR27 - Category: Code Style - Provider Declarations in Modules

By rules, in the module code provider MUST NOT be declared. The only exception is when the module indeed need different instances of the same kind of provider(Eg. manipulating resources across different locations or accounts), you MUST declare configuration_aliases in terraform.required_providers. See details in this document.

provider block declared in the module MUST only be used to differentiate instances used in resource and data. Declaration of fields other than alias in provider block is strictly forbidden. It could lead to module users unable to utilize count, for_each or depends_on. Configurations of the provider instance SHOULD be passed in by the module users.

Good examples:

In verified module:

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
      configuration_aliases = [ azurerm.alternate ]
    }
  }
}

In the root module where we call this verified module:

provider "azurerm" {
  features {}
}

provider "azurerm" {
  alias = "alternate"
  features {}
}

module "foo" {
  source = "xxx"
  providers = {
    azurerm = azurerm
    azurerm.alternate = azurerm.alternate
  }
}

Bad example:

In verified module:

provider "azurerm" {
  # Configuration options
  features {}
}



See origin...

ID: TFNFR30 - Category: Code Style - Handling Deprecated Outputs

Sometimes we notice that the name of certain output is not appropriate anymore, however, since we have to ensure forward compatibility in the same major version, its name MUST NOT be changed directly. It MUST be moved to an independent deprecated_outputs.tf file, then redefine a new output in output.tf and make sure it’s compatible everywhere else in the module.

A cleanup SHOULD be performed to deprecated_outputs.tf and other logics related to compatibility during a major version upgrade.




See origin...

ID: TFNFR31 - Category: Code Style - locals.tf for Locals Only

In locals.tf, file we could declare multiple locals blocks, but only locals blocks are allowed.

You MAY declare locals blocks next to a resource block or data block for some advanced scenarios, like making a fake module to execute some light-weight tests aimed at the expressions.




See origin...

ID: TFNFR33 - Category: Code Style - Precise Local Types

Precise local types SHOULD be used.

Good example:

{
  name = "John"
  age  = 52
}

Bad example:

{
  name = "John"
  age  = "52" # age should be number
}



See origin...

ID: TFNFR34 - Category: Code Style - Using Feature Toggles

A toggle variable MUST be used to allow users to avoid the creation of a new resource block by default if it is added in a minor or patch version.

E.g., our previous release was v1.2.1 and next release would be v1.3.0, now we’d like to submit a pull request which contains such new resource:

resource "azurerm_route_table" "this" {
  location            = local.location
  name                = coalesce(var.new_route_table_name, "${var.subnet_name}-rt")
  resource_group_name = var.resource_group_name
}

A user who’s just upgraded the module’s version would be surprised to see a new resource to be created in a newly generated plan file.

A better approach is adding a feature toggle to be turned off by default:

variable "create_route_table" {
  type     = bool
  default  = false
  nullable = false
}

resource "azurerm_route_table" "this" {
  count               = var.create_route_table ? 1 : 0
  location            = local.location
  name                = coalesce(var.new_route_table_name, "${var.subnet_name}-rt")
  resource_group_name = var.resource_group_name
}



See origin...

ID: TFNFR35 - Category: Code Style - Reviewing Potential Breaking Changes

Potential breaking(surprise) changes introduced by resource block

  1. Adding a new resource without count or for_each for conditional creation, or creating by default
  2. Adding a new argument assignment with a value other than the default value provided by the provider’s schema
  3. Adding a new nested block without making it dynamic or omitting it by default
  4. Renaming a resource block without one or more corresponding moved blocks
  5. Change resource’s count to for_each, or vice versa

Terraform moved block could be your cure.

Potential breaking changes introduced by variable and output blocks

  1. Deleting(Renaming) a variable
  2. Changing type in a variable block
  3. Changing the default value in a variable block
  4. Changing variable’s nullable to false
  5. Changing variable’s sensitive from false to true
  6. Adding a new variable without default
  7. Deleting an output
  8. Changing an output’s value
  9. Changing an output’s sensitive value

These changes do not necessarily trigger breaking changes, but they are very likely to, they MUST be reviewed with caution.




See origin...

ID: TFNFR36 - Category: Code Style - Setting prevent_deletion_if_contains_resources

From Terraform AzureRM 3.0, the default value of prevent_deletion_if_contains_resources in provider block is true. This will lead to an unstable test because the test subscription has some policies applied, and they will add some extra resources during the run, which can cause failures during destroy of resource groups.

Since we cannot guarantee our testing environment won’t be applied some Azure Policy Remediation Tasks in the future, for a robust testing environment, prevent_deletion_if_contains_resources SHOULD be explicitly set to false.




See origin...

ID: TFNFR37 - Category: Code Style - Tool Usage by Module Owner

newres is a command-line tool that generates Terraform configuration files for a specified resource type. It automates the process of creating variables.tf and main.tf files, making it easier to get started with Terraform and reducing the time spent on manual configuration.

Module owners MAY use newres when they’re trying to add new resource block, attribute, or nested block. They MAY generate the whole block along with the corresponding variable blocks in an empty folder, then copy-paste the parts they need with essential refactoring.




Inputs / Outputs

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR14Data TypesSHOULDOwnerContributorBAU
2SNFR22Parameters/Variables for Resource IDsMUSTOwnerContributorBAU
3SNFR26Output - Parameters - DecoratorsMUSTOwnerContributorBAU
4PMNFR5Parameter/Variable NamingSHOULDOwnerContributorBAU
5TFFR2Additional Terraform OutputsSHOULDOwnerContributorBAU
6TFNFR14Not allowed variablesMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.




See origin...

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}, SHOULD contain ResourceId/resource_id in its parameter/variable name when that parameter/variable is part of a user-defined type. This assists 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 a user-defined type: in Bicep its parameter name should be workspaceResourceId and the variable name in Terraform should be workspace_resource_id.

In that user-defined context, workspaceId is not descriptive enough and is ambiguous as to which ID is required to be input.

Special considerations for Bicep

If the property is nested in a parameter and you opt for a resource-derived type (that is, a schema defined by the resource provider), this requirement does not apply. We do however recommend to use a user-defined type whenever these cases occur to increase the module’s usability.

Example for the property subnetArmId of the Cognitive Service’s property networkInjections:

If using a user-defined type, you may define a type for the networkInjections parameter like

param networkInjections networkInjectionType?

@export()
type networkInjectionType = {
  subnetResourceId: string

  // (...)
}

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: [{
      subnetArmId: networkInjections.?subnetResourceId
      // (...)
    }]
  }
}

or a resource-derived type like

param networkInjections resourceInput<'Microsoft.CognitiveServices/accounts@2025-06-01'>.properties.networkInjections

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: networkInjections
  }
}



See origin...

ID: SNFR26 - Output-Parameters - Decorators

Output parameters MUST implement:

Output parameters
@description('The resourceId of your resource.')
output sampleResourceId string = sampleResource.id

@description('The key of your resource.')
@secure()
output sampleResourceKey string = sampleResource.key
# Resource output
output "foo" {
  description = "MyResource foo attribute"
  value = azurerm_resource_myresource.foo
}

# Output of a sensitive attribute
output "bar" {
  description = "MyResource bar attribute"
  value     = azurerm_resource_myresource.bar
  sensitive = true
}



See origin...

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

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




See origin...

ID: TFFR2 - Category: Outputs - Additional Terraform Outputs

Authors SHOULD NOT output entire resource objects as these may contain sensitive outputs and the schema can change with API or provider versions.
Instead, authors SHOULD output the computed attributes of the resource as discreet outputs.
This kind of pattern protects against provider schema changes and is known as an anti-corruption layer.

Remember, you SHOULD NOT output values that are already inputs (other than name).

E.g.,

# Resource output, computed attribute.
output "foo" {
  description = "MyResource foo attribute"
  value = azurerm_resource_myresource.foo
}

# Resource output for resources that are deployed using `for_each`. Again only computed attributes.
output "childresource_foos" {
  description = "MyResource children's foo attributes"
  value = {
    for key, value in azurerm_resource_mychildresource : key => value.foo
  }
}

# Output of a sensitive attribute
output "bar" {
  description = "MyResource bar attribute"
  value     = azurerm_resource_myresource.bar
  sensitive = true
}



See origin...

ID: TFNFR14 - Category: Inputs - Not allowed variables

Since Terraform 0.13, count, for_each and depends_on are introduced for modules, module development is significantly simplified. Module’s owners MUST NOT add variables like enabled or module_depends_on to control the entire module’s operation. Boolean feature toggles are acceptable however.




Testing

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR1Prescribed TestsMUSTOwnerContributorBAU
2SNFR2E2E TestingMUSTOwnerContributorBAU
3SNFR3AVM Compliance TestsMUSTOwnerContributorInitial
4SNFR4Unit TestsSHOULDOwnerContributorBAU
5SNFR5Upgrade TestsSHOULDOwnerContributorBAU
6SNFR6Static Analysis/Linting TestsMUSTOwnerContributorBAU
7SNFR7Idempotency TestsMUSTOwnerContributorBAU
8SNFR24Testing Child, Extension & Interface ResourcesMUSTOwnerContributorBAU
9TFNFR5Test ToolingMUSTOwnerContributorBAU
10TFNFR15Variable Definition OrderSHOULDOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: SNFR1 - Category: Testing - Prescribed Tests

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




See origin...

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.

Tip

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

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 & Bicep Log Analytics Workspace examples using simple/native declarations for use in E2E tests

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
  }
}
Skipping Deployments (SHOULD NOT)

Deployment tests are an important part of a module’s validation and a staple of AVM’s CI environment. However, there are situations where certain e2e-test-deployments cannot be performed against AVM’s test environment (e.g., if a special configuration/registration (such as certain AI models) is required). For these cases, the CI offers the possibility to ‘skip’ specific test cases by placing a file named .e2eignore in their test folder.

Note

A skipped test case is still added to the ‘Usage Examples’ section of the module’s readme and should be manually validated in regular intervals.

Details for use in E2E tests

You MUST add a note to the tests metadata description, which explains the excemption.

If you require that a test is skipped and add an “.e2eignore” file (e.g. \<module\>/tests/e2e/\<testname\>/.e2eignore) to a pull request, a member of the AVM Core Technical Bicep Team must approve set pull request. The content of the file is logged the module’s workflow runs and transparently communicates why the test case is skipped during the deployment validation stage. It iss hence important to specify the reason for skipping the deployment in this file.

Sample filecontent:

The test is skipped, as only one instance of this service can be deployed to a subscription.
Note

For resource modules, the ‘defaults’ and ‘waf-aligned’ tests can’t be skipped.

The deployment of a test can be skipped by adding a .e2eignore file into a test folder (e.g. /examples/<testname>).




See origin...

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.

Important

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.




See origin...

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.




See origin...

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.




See origin...

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.




See origin...

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.




See origin...

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

Module owners MUST test that child and extension resources and those Bicep or Terreform interface resources that are supported by their modules, are validated 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.




See origin...

ID: TFNFR5 - Category: Testing - Test Tooling

Module owners MUST use the below tooling for unit/linting/static/security analysis tests. These are also used in the AVM Compliance Tests.




See origin...

ID: TFNFR15 - Category: Code Style - Variable Definition Order

Input variables SHOULD follow this order:

  1. All required fields, in alphabetical order
  2. All optional fields, in alphabetical order

A variable without default value is a required field, otherwise it’s an optional one.




Documentation

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR15Automatic Documentation GenerationMUSTOwnerContributorBAU
2SNFR16Examples/E2EMUSTOwnerContributorBAU
3TFNFR1DescriptionsMUSTOwnerContributorBAU
4TFNFR2Module Documentation GenerationMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.




See origin...

ID: SNFR16 - Category: Documentation - Examples/E2E

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




See origin...

ID: TFNFR1 - Category: Documentation - Descriptions

Where descriptions for variables and outputs spans multiple lines. The description MAY provide variable input examples for each variable using the HEREDOC format and embedded markdown.

Example:

  variable "my_complex_input" {
    type = map(object({
      param1 = string
      param2 = optional(number, null)
    }))
    description = <<DESCRIPTION
  A complex input variable that is a map of objects.
  Each object has two attributes:
  
  - `param1`: A required string parameter.
  - `param2`: (Optional) An optional number parameter.
  
  Example Input:
  
  ```terraform
  my_complex_input = {
    "object1" = {
      param1 = "value1"
      param2 = 2
    }
    "object2" = {
      param1 = "value2"
    }
  }
  ```
  DESCRIPTION
  }
  



See origin...

ID: TFNFR2 - Category: Documentation - Module Documentation Generation

Terraform modules documentation MUST be automatically generated via Terraform Docs.

A file called .terraform-docs.yml MUST be present in the root of the module and have the following content:

  ---
  ### To generate the output file to partially incorporate in the README.md,
  ### Execute this command in the Terraform module's code folder:
  # terraform-docs -c .terraform-docs.yml .
  
  formatter: "markdown document" # this is required
  
  version: "0.16.0"
  
  header-from: "_header.md"
  footer-from: "_footer.md"
  
  recursive:
    enabled: false
    path: modules
  
  sections:
    hide: []
    show: []
  
  content: |-
    {{ .Header }}    
  
    <!-- markdownlint-disable MD033 -->
    {{ .Requirements }}
  
    {{ .Providers }}
  
    {{ .Resources }}
  
    <!-- markdownlint-disable MD013 -->
    {{ .Inputs }}
  
    {{ .Outputs }}
  
    {{ .Modules }}
  
    {{ .Footer }}
  
  output:
    file: README.md
    mode: replace
    template: |-
      <!-- BEGIN_TF_DOCS -->
      {{ .Content }}
      <!-- END_TF_DOCS -->      
  output-values:
    enabled: false
    from: ""
  
  sort:
    enabled: true
    by: required
  
  settings:
    anchor: true
    color: true
    default: true
    description: false
    escape: true
    hide-empty: false
    html: true
    indent: 2
    lockfile: true
    read-comments: true
    required: true
    sensitive: true
    type: true
  



Release / Publishing

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR17Semantic VersioningMUSTOwnerContributorBAU
2SNFR18Breaking ChangesSHOULDOwnerContributorBAU
3SNFR19Registries TargetedMUSTOwnerContributorBAU
4SNFR21Cross Language CollaborationSHOULDOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: SNFR17 - Category: Release - Semantic Versioning

Important

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

Module versioning before first Major version release 1.0.0

  • Initially modules MUST be released as version 0.1.0 and incremented via Minor and Patch versions only until the AVM Core Team are confident the AVM specifications are mature enough and appropriate CI test coverage is in place, plus the module owner is happy the module has been “road tested” and is now stable enough for its first Major release of version 1.0.0.

    Note

    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. 👍

  • Until first Major version 1.0.0 is released, given a version number X.Y.Z:

    • X Major version MUST NOT be bumped.
    • Y Minor version MUST be bumped when introducing breaking changes (which would normally bump Major after 1.0.0 release) or feature updates (same as it will be after 1.0.0 release).
    • Z Patch version MUST be bumped when introducing non-breaking, backward compatible bug fixes (same as it will be after 1.0.0 release).



See origin...

ID: SNFR18 - Category: Release - Breaking Changes

A module SHOULD avoid breaking changes, e.g., deprecating inputs vs. removing. If you need to implement changes that cause a breaking change, the major version should be increased.

Info

Modules that have not been released as 1.0.0 may introduce breaking changes, as explained in the previous ID SNFR17. That means that you have to introduce non-breaking and breaking changes with a minor version jump, as long as the module has not reached version 1.0.0.

There are, however, scenarios where you want to include breaking changes into a commit and not create a new major version. If you want to introduce breaking changes as part of a minor update, you can do so. In this case, it is essential to keep the change backward compatible, so that the existing code will continue to work. At a later point, another update can increase the major version and remove the code introduced for the backward compatibility.

Tip

See the language specific examples to find out how you can deal with deprecations in AVM modules.




See origin...

ID: SNFR19 - Category: Publishing - Registries Targeted

Modules MUST be published to their respective language public registries.

Tip

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




See origin...

ID: SNFR21 - Category: Publishing - Cross Language Collaboration

When the module owners of the same Resource, Pattern or Utility 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.




Terraform Resource Module Specifications

Contribution / Support

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR8Module Owner(s) GitHubMUSTOwnerInitial
2SNFR20GitHub Teams OnlyMUSTOwnerInitial
3SNFR9AVM & PG Teams GitHub Repo PermissionsMUSTOwnerInitial
4SNFR10MIT LicensingMUSTOwnerInitial
5SNFR11Issues Response TimesMUSTOwnerContributorBAU
6SNFR12Versions SupportedMUSTOwnerBAU
7SNFR23GitHub Repo LabelsMUSTOwnerBAU
8PMNFR4Missing Resource Module(s)MUSTOwnerContributorBAU
9TFNFR3GitHub Repo Branch ProtectionMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.

Note

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.




See origin...

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 a GitHub team assigned for module owners. This team MUST be created in the Azure organization in GitHub.

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

Info

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.

Bicep

Important

As part of the module proposal process, the name of the GitHub team for each approved module is already defined in the respective Module Indexes (or CSV file). This team MUST be created (and used) for each module.

Module owners don’t need to construct the name of the GitHub team for their module themselves, instead they need use the name prescribed in the related CSV file, at the time of approval.

For a direct link, see the list of related index pages:

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!

Naming Convention

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

  • <hyphenated module name>-module-owners-bicep - to grant permissions for module owners on Bicep modules

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 == the role the GitHub Team is assigned to
  • <bicep == the language the module is written in

Examples:

  • avm-res-compute-virtualmachine-module-owners-bicep
Note

The naming convention for Bicep modules is slightly different than the naming convention for their respective GitHub teams.

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.

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

Grant permissions through team memberships

Note

In case of Bicep modules, permissions to the BRM repository (the repo of the Bicep Registry) are granted via assigning the -module-owners- 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 team to the respective parent, only the AVM core team can approve this parent-child relationship.

Module owners MUST create their -module-owners- team and as part of the provisioning process, they MUST request the addition of this team to its respective parent team (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.

Example - GitHub team 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.
Tip

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).

Note

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 Bicep reviewers, with the @Azure/ prefix, i.e., @Azure/avm-module-reviewers-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-module-reviewers-bicep

Terraform

Note

Access management for Terraform repositories now uses a single team, membership of which is managed using an internal entitlement management tool (Core Identity).

All module owners MUST request access to the avm-module-owners-terraform GitHub team via the Azure Verified Module Owners Terraform entitlement in Core Identity (Microsoft internal tool).




See origin...

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

Note

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

Terraform

Important

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.




See origin...

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

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




See origin...

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

A module owner MUST respond to logged issues as defined in the support statement. See Module Support for more information.




See origin...

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.




See origin...

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:

➕ AVM Standard GitHub 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 📣In the AVM repository: this module needs an owner to develop or maintain it. In the BRM repository: the module owner needs to review a PR.
FF0019
Needs: Module Contributor 📣This module needs secondary owner(s) or contributor(s) to develop or maintain it
C95474
Needs: Core Team 🧞‍♂️This item needs the AVM Core Team to review it
DB4503
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 Deprecated 🔴This is a request to deprecate a module
000000
Status: Module Orphaned 🟡The module has no owner and is therefore orphaned at this time
F4A460
Status: Ready For Repository Creation 📝This module is approved and the owner is ready for the repository to be created (Terraform)
136A41
Status: Repository Created 📄This module has had it's repository created and configured ready for owner contribution (Terraform)
27AB03
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: CI 🚀This issue is related to the AVM CI
74CFB0
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
Class: Utility Module 📦This is a utility module
CAD1DE
Class: Child Module 📦This is a child module
5E5186

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

➕ Set-AvmGitHubLabels.ps1

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
```shell
# 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
  



See origin...

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.

Exception

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




See origin...

ID: TFNFR3 - Category: Contribution/Support - GitHub Repo Branch Protection

Module owners MUST set a branch protection policy on their GitHub Repositories for AVM modules against their default branch, typically main, to do the following:

  1. Requires a Pull Request before merging
  2. Require approval of the most recent reviewable push
  3. Dismiss stale pull request approvals when new commits are pushed
  4. Require linear history
  5. Prevents force pushes
  6. Not allow deletions
  7. Require CODEOWNERS review
  8. Do not allow bypassing the above settings
  9. Above settings MUST also be enforced to administrators
Tip

If you use the template repository as mentioned in the contribution guide, the above will automatically be set.




Telemetry

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR3Deployment/Usage TelemetryMUSTOwnerInitial
2SFR4Telemetry Enablement FlexibilityMUSTOwnerInitial
➕ See Specifications for this category
See origin...

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

Modules MUST provide the capability to collect deployment/usage telemetry as detailed in Telemetry further.

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.)

Telemetry Information Notice

Note

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

  • Bicep: Using the 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.

Module Class Applicability

This specification applies to all AVM module classes (resource, pattern, utility), however, in case of utility modules, telemetry collection MUST only be added when the utility module deploys any resources (e.g., a deployment script resource). If the utility module does not deploy any resources, telemetry collection MUST NOT be added.

Bicep

Important

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.

The value you need to use for your module is defined in the related module index. You can look it up on the index pages for Resource Modules, Pattern Modules and Utility Modules.

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

  • <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-
    • The prefixes: avm-ptn-
  • <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)
Note

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.

An example deployment name for the AVM Virtual Machine Resource Module would be: 46d3xbcp.res.compute-virtualmachine.1-2-3.eum3

An example deployment name for a shortened module name would be: 46d3xbcp.res.desktopvirtualization-appgroup.1-2-3.eum3

Tip

Terraform: Terraform uses a telemetry provider, the configuration of which is the same for every module and is included in the template repo.

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

Terraform

To enable telemetry data collection for Terraform modules, the modtm telemetry provider MUST be used. 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.

The modtm provider MUST be 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"
    }
  }
}



See origin...

ID: SFR4 - Category: Telemetry - Telemetry Enablement Flexibility

The telemetry collection MUST be on/enabled by default, however module consumers MUST be allowed to disable it by setting the below parameter/variable value to false:

  • Bicep: enableTelemetry
  • Terraform: enable_telemetry
Note

Whenever a module references AVM modules that implement the telemetry parameter (e.g., a pattern module that uses AVM resource modules), the telemetry parameter value MUST be passed through to these modules. This is necessary to ensure a consumer can reliably enable & disable the telemetry feature for all used modules.

This general specification can be modified for some use-cases, that are language specific:

Bicep

For cross-references in resource modules, the spec BCPFR7 also applies.

Terraform

Currently, no further requirements apply.




Naming / Composition

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SFR1Preview ServicesMUSTOwnerBAU
2SFR2WAF AlignedSHOULDOwnerBAU
3SFR5Availability ZonesMUSTOwnerInitial
4SFR6Data RedundancyMUSTOwnerInitial
5SNFR25Resource NamingMUSTOwnerInitial
6RMFR1Single Resource OnlyMUSTOwnerContributorBAU
7RMFR2No Resource Wrapper ModulesMUSTOwnerInitial
8RMFR3Resource GroupsMUSTOwnerContributorBAU
9RMFR4AVM Consistent Feature & Extension Resources Value AddMUSTOwnerContributorBAU
10RMFR5AVM Consistent Feature & Extension Resources Value Add Interfaces/SchemasMUSTOwnerContributorBAU
11RMFR8Dependency on child and other resourcesMUSTOwnerContributorBAU
12RMFR9End-of-life resource versionsSHOULDOwnerContributorBAU
13RMNFR1Module NamingMUSTOwnerInitial
14RMNFR3RP CollaborationSHOULDOwnerBAU
15TFFR1Cross-Referencing ModulesMUSTOwnerContributorBAU
16TFFR3Providers - Permitted VersionsMUSTOwnerContributorBAU
17TFNFR4Lower snake_casingMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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”



See origin...

ID: SFR2 - Category: Composition - WAF Aligned

Modules SHOULD set defaults in input parameters/variables to align to high priority/impact/severity 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.

Tip

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




See origin...

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.

Note

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




See origin...

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.

Note

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




See origin...

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>.

Tip

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.




See origin...

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.




See origin...

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

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




See origin...

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.




See origin...

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.

Note

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.

Tip

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




See origin...

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:




See origin...

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.




See origin...

ID: RMFR9 - Category: Composition - End-of-life resource versions

When a given version of an Azure resource used in a resource module reaches its end-of-life (EOL) and is no longer supported by Microsoft, the module owner SHOULD ensure that:

  1. The module is aligned with these changes and only includes supported versions of the resource. This is typically achieved through the allowed values in the parameter that specifies the resource SKU or type.
  2. The following notice is shown under the Notes section of the module’s readme.md. (If any related public announcement is available, it can also be linked to from the Notes section.):

    “Certain versions of this Azure resource reached their end of life. The latest version of this module only includes supported versions of the resource. All unsupported versions have been removed from the related parameters.”

  3. AND the related parameter’s description:

    “Certain versions of this Azure resource reached their end of life. The latest version of this module only includes supported versions of the resource. All unsupported versions have been removed from this parameter.”




See origin...

ID: RMNFR1 - Category: Naming - Module Naming

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

Important

As part of the module proposal process, the module’s approved name is captured both in the module proposal issue AND the related module index page (backed by the corresponding CSV file).

Therefore, module owners don’t need to construct the module’s name themselves, instead they need use the name prescribed in the module proposal issue or in the related CSV file, at the time of approval.

Note

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.

Bicep Resource Module Naming

  • Naming convention (module name for registry): avm/res/<hyphenated resource provider name>/<hyphenated ARM resource type>
  • 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.

Bicep Child Module Naming

  • Naming convention (module name for registry):avm/res/<hyphenated resource provider name>/<hyphenated ARM resource type>/ <hyphenated child resource type/<hyphenated grandchild resource type>/<etc.>

  • Example: avm/res/network/virtual-network/subnet or avm/res/storage/storage-account/blob-service/container

  • 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.Network = network.
    • <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.Network/virtualNetworks = virtual-network.
    • <hyphenated child resource type (to be repeated for grandchildren, etc.)> 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.Network/virtualNetworks/subnets = subnet or Microsoft.Storage/storageAccounts/blobServices/containers = blob-service/container.

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



See origin...

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.




See origin...

ID: TFFR1 - Category: Composition - Cross-Referencing Modules

Module owners MAY cross-references other modules to build either Resource or Pattern modules. However, they MUST be referenced only by a HashiCorp Terraform registry reference to a pinned version e.g.,

module "other-module" {
  source  = "Azure/xxx/azurerm"
  version = "1.2.3"
}

They MUST NOT use git reference to a module.

module "other-module" {
  source = "git::https://xxx.yyy/xxx.git"
}
module "other-module" {
  source = "github.com/xxx/yyy"
}

Modules MUST NOT contain references to non-AVM modules.

Tip

See Module Sources for more information.




See origin...

ID: TFFR3 - Category: Providers - Permitted Versions

Authors MUST only use the following Azure providers, and versions, in their modules:

providermin versionmax version
azapi>= 2.0< 3.0
azurerm>= 4.0< 5.0
Note

Authors MAY select either Azurerm, Azapi, or both providers in their module.

Authors MUST use the required_providers block in their module to enforce the provider versions.

The following is an example.

terraform {
  required_providers {
    # Include one or both providers, as needed
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
    azapi = {
      source  = "Azure/azapi"
      version = "~> 2.0"
    }
  }
}



See origin...

ID: TFNFR4 - Category: Composition - Code Styling - lower snake_casing

Module owners MUST use lower snake_casing for naming the following:

  • Locals
  • Variables
  • Outputs
  • Resources (symbolic names)
  • Modules (symbolic names)

For example: snake_casing_example (every word in lowercase, with each word separated by an underscore _)




Code Style

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1TFNFR6Resource & Data OrderSHOULDOwnerContributorBAU
2TFNFR7Count & for_each UseMUSTOwnerContributorBAU
3TFNFR8Resource & Data Block OrdersSHOULDOwnerContributorBAU
4TFNFR9Module Block OrderSHOULDOwnerContributorBAU
5TFNFR10No Double Quotes in ignore_changesMUSTOwnerContributorBAU
6TFNFR11Null Comparison ToggleSHOULDOwnerContributorBAU
7TFNFR12Dynamic for Optional Nested ObjectsMUSTOwnerContributorBAU
8TFNFR13Default Values with coalesce/trySHOULDOwnerContributorBAU
9TFNFR16Variable Naming RulesSHOULDOwnerContributorBAU
10TFNFR17Variables with DescriptionsSHOULDOwnerContributorBAU
11TFNFR18Variables with TypesMUSTOwnerContributorBAU
12TFNFR19Sensitive Data VariablesSHOULDOwnerContributorBAU
13TFNFR20Non-Nullable Defaults for collection valuesSHOULDOwnerContributorBAU
14TFNFR21Discourage Nullability by DefaultMUSTOwnerContributorBAU
15TFNFR22Avoid sensitive = falseMUSTOwnerContributorBAU
16TFNFR23Sensitive Default Value ConditionsMUSTOwnerContributorBAU
17TFNFR24Handling Deprecated VariablesMUSTOwnerContributorBAU
18TFNFR25Verified Modules RequirementsMUSTOwnerContributorBAU
19TFNFR26Providers in required_providersMUSTOwnerContributorBAU
20TFNFR27Provider Declarations in ModulesMUSTOwnerContributorBAU
22TFNFR30Handling Deprecated OutputsMUSTOwnerContributorBAU
23TFNFR31locals.tf for Locals OnlyMAYOwnerContributorBAU
25TFNFR33Precise Local TypesSHOULDOwnerContributorBAU
26TFNFR34Using Feature TogglesMUSTOwnerContributorBAU
27TFNFR35Reviewing Potential Breaking ChangesMUSTOwnerContributorBAU
28TFNFR36Setting prevent_deletion_if_contains_resourcesSHOULDOwnerContributorBAU
29TFNFR37Tool Usage by Module OwnerMAYOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: TFNFR6 - Category: Code Style - Resource & Data Order

For the definition of resources in the same file, the resources be depended on SHOULD come first, after them are the resources depending on others.

Resources that have dependencies SHOULD be defined close to each other.




See origin...

ID: TFNFR7 - Category: Code Style - count & for_each Use

We can use count and for_each to deploy multiple resources, but the improper use of count can lead to anti pattern.

You can use count to create some kind of resources under certain conditions, for example:

resource "azurerm_network_security_group" "this" {
  count               = local.create_new_security_group ? 1 : 0
  name                = coalesce(var.new_network_security_group_name, "${var.subnet_name}-nsg")
  resource_group_name = var.resource_group_name
  location            = local.location
  tags                = var.new_network_security_group_tags
}

The module’s owners MUST use map(xxx) or set(xxx) as resource’s for_each collection, the map’s key or set’s element MUST be static literals.

Good example:

resource "azurerm_subnet" "pair" {
  for_each             = var.subnet_map // `map(string)`, when user call this module, it could be: `{ "subnet0": "subnet0" }`, or `{ "subnet0": azurerm_subnet.subnet0.name }`
  name                 = "${each.value}"-pair
  resource_group_name  = azurerm_resource_group.example.name
  virtual_network_name = azurerm_virtual_network.example.name
  address_prefixes     = ["10.0.1.0/24"]
}

Bad example:

resource "azurerm_subnet" "pair" {
  for_each             = var.subnet_name_set // `set(string)`, when user use `toset([azurerm_subnet.subnet0.name])`, it would cause an error.
  name                 = "${each.value}"-pair
  resource_group_name  = azurerm_resource_group.example.name
  virtual_network_name = azurerm_virtual_network.example.name
  address_prefixes     = ["10.0.1.0/24"]
}



See origin...

ID: TFNFR8 - Category: Code Style - Resource & Data Block Orders

There are 3 types of assignment statements in a resource or data block: argument, meta-argument and nested block. The argument assignment statement is a parameter followed by =:

location = azurerm_resource_group.example.location

or:

tags = {
  environment = "Production"
}

Nested block is a assignment statement of parameter followed by {} block:

subnet {
  name           = "subnet1"
  address_prefix = "10.0.1.0/24"
}

Meta-arguments are assignment statements can be declared by all resource or data blocks. They are:

  • count
  • depends_on
  • for_each
  • lifecycle
  • provider

The order of declarations within resource or data blocks is:

All the meta-arguments SHOULD be declared on the top of resource or data blocks in the following order:

  1. provider
  2. count
  3. for_each

Then followed by:

  1. required arguments
  2. optional arguments
  3. required nested blocks
  4. optional nested blocks

All ranked in alphabetical order.

These meta-arguments SHOULD be declared at the bottom of a resource block with the following order:

  1. depends_on
  2. lifecycle

The parameters of lifecycle block SHOULD show up in the following order:

  1. create_before_destroy
  2. ignore_changes
  3. prevent_destroy

parameters under depends_on and ignore_changes are ranked in alphabetical order.

Meta-arguments, arguments and nested blocked are separated by blank lines.

dynamic nested blocks are ranked by the name comes after dynamic, for example:

  dynamic "linux_profile" {
    for_each = var.admin_username == null ? [] : ["linux_profile"]

    content {
      admin_username = var.admin_username

      ssh_key {
        key_data = replace(coalesce(var.public_ssh_key, tls_private_key.ssh[0].public_key_openssh), "\n", "")
      }
    }
  }

This dynamic block will be ranked as a block named linux_profile.

Code within a nested block will also be ranked following the rules above.

PS: You can use avmfix tool to reformat your code automatically.




See origin...

ID: TFNFR9 - Category: Code Style - Module Block Order

The meta-arguments below SHOULD be declared on the top of a module block with the following order:

  1. source
  2. version
  3. count
  4. for_each

blank lines will be used to separate them.

After them will be required arguments, optional arguments, all ranked in alphabetical order.

These meta-arguments below SHOULD be declared on the bottom of a resource block in the following order:

  1. depends_on
  2. providers

Arguments and meta-arguments SHOULD be separated by blank lines.




See origin...

ID: TFNFR10 - Category: Code Style - No Double Quotes in ignore_changes

The ignore_changes attribute MUST NOT be enclosed in double quotes.

Good example:

lifecycle {
    ignore_changes = [
      tags,
    ]
}

Bad example:

lifecycle {
    ignore_changes = [
      "tags",
    ]
}



See origin...

ID: TFNFR11 - Category: Code Style - Null Comparison Toggle

Sometimes we need to ensure that the resources created are compliant to some rules at a minimum extent, for example a subnet has to be connected to at least one network_security_group. The user SHOULD pass in a security_group_id and ask us to make a connection to an existing security_group, or want us to create a new security group.

Intuitively, we will define it like this:

variable "security_group_id" {
  type: string
}

resource "azurerm_network_security_group" "this" {
  count               = var.security_group_id == null ? 1 : 0
  name                = coalesce(var.new_network_security_group_name, "${var.subnet_name}-nsg")
  resource_group_name = var.resource_group_name
  location            = local.location
  tags                = var.new_network_security_group_tags
}

The disadvantage of this approach is if the user create a security group directly in the root module and use the id as a variable of the module, the expression which determines the value of count will contain an attribute from another resource, the value of this very attribute is “known after apply” at plan stage. Terraform core will not be able to get an exact plan of deployment during the “plan” stage.

You can’t do this:

resource "azurerm_network_security_group" "foo" {
  name                = "example-nsg"
  resource_group_name = "example-rg"
  location            = "eastus"
}

module "bar" {
  source = "xxxx"
  ...
  security_group_id = azurerm_network_security_group.foo.id
}

For this kind of parameters, wrapping with object type is RECOMMENDED:

variable "security_group" {
  type: object({
    id   = string
  })
  default     = null
}

The advantage of doing so is encapsulating the value which is “known after apply” in an object, and the object itself can be easily found out if it’s null or not. Since the id of a resource cannot be null, this approach can avoid the situation we are facing in the first example, like the following:

resource "azurerm_network_security_group" "foo" {
  name                = "example-nsg"
  resource_group_name = "example-rg"
  location            = "eastus"
}

module "bar" {
  source = "xxxx"
  ...
  security_group = {
    id = azurerm_network_security_group.foo.id
  }
}

This technique SHOULD be used under this use case only.




See origin...

ID: TFNFR12 - Category: Code Style - Dynamic for Optional Nested Objects

An example from the community:

resource "azurerm_kubernetes_cluster" "main" {
  ...
  dynamic "identity" {
    for_each = var.client_id == "" || var.client_secret == "" ? [1] : []

    content {
      type                      = var.identity_type
      user_assigned_identity_id = var.user_assigned_identity_id
    }
  }
  ...
}

Please refer to the coding style in the example. Nested blocks under conditions, MUST be declared as:

for_each = <condition> ? [<some_item>] : []



See origin...

ID: TFNFR13 - Category: Code Style - Default Values with coalesce/try

The following example shows how "${var.subnet_name}-nsg" SHOULD be used when var.new_network_security_group_name is null or ""

Good examples:

coalesce(var.new_network_security_group_name, "${var.subnet_name}-nsg")
try(coalesce(var.new_network_security_group.name, "${var.subnet_name}-nsg"), "${var.subnet_name}-nsg")

Bad examples:

var.new_network_security_group_name == null ? "${var.subnet_name}-nsg" : var.new_network_security_group_name)



See origin...

ID: TFNFR16 - Category: Code Style - Variable Naming Rules

The naming of a variable SHOULD follow HashiCorp’s naming rule.

variable used as feature switches SHOULD apply a positive statement, use xxx_enabled instead of xxx_disabled. Avoid double negatives like !xxx_disabled.

Please use xxx_enabled instead of xxx_disabled as name of a variable.




See origin...

ID: TFNFR17 - Category: Code Style - Variables with Descriptions

The target audience of description is the module users.

For a newly created variable (Eg. variable for switching dynamic block on-off), it’s description SHOULD precisely describe the input parameter’s purpose and the expected data type. description SHOULD NOT contain any information for module developers, this kind of information can only exist in code comments.

For object type variable, description can be composed in HEREDOC format:

variable "kubernetes_cluster_key_management_service" {
  type: object({
    key_vault_key_id         = string
    key_vault_network_access = optional(string)
  })
  default     = null
  description = <<-EOT
  - `key_vault_key_id` - (Required) Identifier of Azure Key Vault key. See [key identifier format](https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#vault-name-and-object-name) for more details. When Azure Key Vault key management service is enabled, this field is required and must be a valid key identifier. When `enabled` is `false`, leave the field empty.
  - `key_vault_network_access` - (Optional) Network access of the key vault Network access of key vault. The possible values are `Public` and `Private`. `Public` means the key vault allows public access from all networks. `Private` means the key vault disables public access and enables private link. Defaults to `Public`.
EOT
}



See origin...

ID: TFNFR18 - Category: Code Style - Variables with Types

type MUST be defined for every variable. type SHOULD be as precise as possible, any MAY only be defined with adequate reasons.

  • Use bool instead of string or number for true/false
  • Use string for text
  • Use concrete object instead of map(any)



See origin...

ID: TFNFR19 - Category: Code Style - Sensitive Data Variables

If variable’s type is object and contains one or more fields that would be assigned to a sensitive argument, then this whole variable SHOULD be declared as sensitive = true, otherwise you SHOULD extract sensitive field into separated variable block with sensitive = true.




See origin...

ID: TFNFR20 - Category: Code Style - Non-Nullable Defaults for collection values

Nullable SHOULD be set to false for collection values (e.g. sets, maps, lists) when using them in loops. However for scalar values like string and number, a null value MAY have a semantic meaning and as such these values are allowed.




See origin...

ID: TFNFR21 - Category: Code Style - Discourage Nullability by Default

nullable = true MUST be avoided.




See origin...

ID: TFNFR22 - Category: Code Style - Avoid sensitive = false

sensitive = false MUST be avoided.




See origin...

ID: TFNFR23 - Category: Code Style - Sensitive Default Value Conditions

A default value MUST NOT be set for a sensitive input - e.g., a default password.




See origin...

ID: TFNFR24 - Category: Code Style - Handling Deprecated Variables

Sometimes we will find names for some variable are not suitable anymore, or a change SHOULD be made to the data type. We want to ensure forward compatibility within a major version, so direct changes are strictly forbidden. The right way to do this is move this variable to an independent deprecated_variables.tf file, then redefine the new parameter in variable.tf and make sure it’s compatible everywhere else.

Deprecated variable MUST be annotated as DEPRECATED at the beginning of the description, at the same time the replacement’s name SHOULD be declared. E.g.,

variable "enable_network_security_group" {
  type        = string
  default     = null
  description = "DEPRECATED, use `network_security_group_enabled` instead; Whether to generate a network security group and assign it to the subnet. Changing this forces a new resource to be created."
}

A cleanup of deprecated_variables.tf SHOULD be performed during a major version release.




See origin...

ID: TFNFR25 - Category: Code Style - Verified Modules Requirements

The terraform.tf file MUST only contain one terraform block.

The first line of the terraform block MUST define a required_version property for the Terraform CLI.

The required_version property MUST include a constraint on the minimum version of the Terraform CLI. Previous releases of the Terraform CLI can have unexpected behavior.

The required_version property MUST include a constraint on the maximum major version of the Terraform CLI. Major version releases of the Terraform CLI can introduce breaking changes and MUST be tested.

The required_version property constraint SHOULD use the ~> #.# or the >= #.#.#, < #.#.# format.

Note: You can read more about Terraform version constraints in the documentation.

Example terraform.tf file:

terraform {
  required_version = "~> 1.6"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.11"
    }
  }
}



See origin...

ID: TFNFR26 - Category: Code Style - Providers in required_providers

The terraform block in terraform.tf MUST contain the required_providers block.

Each provider used directly in the module MUST be specified with the source and version properties. Providers in the required_providers block SHOULD be sorted in alphabetical order.

Do not add providers to the required_providers block that are not directly required by this module. If submodules are used then each submodule SHOULD have its own versions.tf file.

The source property MUST be in the format of namespace/name. If this is not explicitly specified, it can cause failure.

The version property MUST include a constraint on the minimum version of the provider. Older provider versions may not work as expected.

The version property MUST include a constraint on the maximum major version. A provider major version release may introduce breaking change, so updates to the major version constraint for a provider MUST be tested.

The version property constraint SHOULD use the ~> #.# or the >= #.#.#, < #.#.# format.

Note: You can read more about Terraform version constraints in the documentation.

Good examples:

terraform {
  required_version = "~> 1.6"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }
}
terraform {
  required_version = ">= 1.6.6, < 2.0.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.11.1, < 4.0.0"
    }
  }
}
terraform {
  required_version = ">= 1.6, < 2.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.11, < 4.0"
    }
  }
}

Acceptable example (but not recommended):

terraform {
  required_version = "1.6"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "3.11"
    }
  }
}

Bad example:

terraform {
  required_version = ">= 1.6"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.11"
    }
  }
}



See origin...

ID: TFNFR27 - Category: Code Style - Provider Declarations in Modules

By rules, in the module code provider MUST NOT be declared. The only exception is when the module indeed need different instances of the same kind of provider(Eg. manipulating resources across different locations or accounts), you MUST declare configuration_aliases in terraform.required_providers. See details in this document.

provider block declared in the module MUST only be used to differentiate instances used in resource and data. Declaration of fields other than alias in provider block is strictly forbidden. It could lead to module users unable to utilize count, for_each or depends_on. Configurations of the provider instance SHOULD be passed in by the module users.

Good examples:

In verified module:

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
      configuration_aliases = [ azurerm.alternate ]
    }
  }
}

In the root module where we call this verified module:

provider "azurerm" {
  features {}
}

provider "azurerm" {
  alias = "alternate"
  features {}
}

module "foo" {
  source = "xxx"
  providers = {
    azurerm = azurerm
    azurerm.alternate = azurerm.alternate
  }
}

Bad example:

In verified module:

provider "azurerm" {
  # Configuration options
  features {}
}



See origin...

ID: TFNFR30 - Category: Code Style - Handling Deprecated Outputs

Sometimes we notice that the name of certain output is not appropriate anymore, however, since we have to ensure forward compatibility in the same major version, its name MUST NOT be changed directly. It MUST be moved to an independent deprecated_outputs.tf file, then redefine a new output in output.tf and make sure it’s compatible everywhere else in the module.

A cleanup SHOULD be performed to deprecated_outputs.tf and other logics related to compatibility during a major version upgrade.




See origin...

ID: TFNFR31 - Category: Code Style - locals.tf for Locals Only

In locals.tf, file we could declare multiple locals blocks, but only locals blocks are allowed.

You MAY declare locals blocks next to a resource block or data block for some advanced scenarios, like making a fake module to execute some light-weight tests aimed at the expressions.




See origin...

ID: TFNFR33 - Category: Code Style - Precise Local Types

Precise local types SHOULD be used.

Good example:

{
  name = "John"
  age  = 52
}

Bad example:

{
  name = "John"
  age  = "52" # age should be number
}



See origin...

ID: TFNFR34 - Category: Code Style - Using Feature Toggles

A toggle variable MUST be used to allow users to avoid the creation of a new resource block by default if it is added in a minor or patch version.

E.g., our previous release was v1.2.1 and next release would be v1.3.0, now we’d like to submit a pull request which contains such new resource:

resource "azurerm_route_table" "this" {
  location            = local.location
  name                = coalesce(var.new_route_table_name, "${var.subnet_name}-rt")
  resource_group_name = var.resource_group_name
}

A user who’s just upgraded the module’s version would be surprised to see a new resource to be created in a newly generated plan file.

A better approach is adding a feature toggle to be turned off by default:

variable "create_route_table" {
  type     = bool
  default  = false
  nullable = false
}

resource "azurerm_route_table" "this" {
  count               = var.create_route_table ? 1 : 0
  location            = local.location
  name                = coalesce(var.new_route_table_name, "${var.subnet_name}-rt")
  resource_group_name = var.resource_group_name
}



See origin...

ID: TFNFR35 - Category: Code Style - Reviewing Potential Breaking Changes

Potential breaking(surprise) changes introduced by resource block

  1. Adding a new resource without count or for_each for conditional creation, or creating by default
  2. Adding a new argument assignment with a value other than the default value provided by the provider’s schema
  3. Adding a new nested block without making it dynamic or omitting it by default
  4. Renaming a resource block without one or more corresponding moved blocks
  5. Change resource’s count to for_each, or vice versa

Terraform moved block could be your cure.

Potential breaking changes introduced by variable and output blocks

  1. Deleting(Renaming) a variable
  2. Changing type in a variable block
  3. Changing the default value in a variable block
  4. Changing variable’s nullable to false
  5. Changing variable’s sensitive from false to true
  6. Adding a new variable without default
  7. Deleting an output
  8. Changing an output’s value
  9. Changing an output’s sensitive value

These changes do not necessarily trigger breaking changes, but they are very likely to, they MUST be reviewed with caution.




See origin...

ID: TFNFR36 - Category: Code Style - Setting prevent_deletion_if_contains_resources

From Terraform AzureRM 3.0, the default value of prevent_deletion_if_contains_resources in provider block is true. This will lead to an unstable test because the test subscription has some policies applied, and they will add some extra resources during the run, which can cause failures during destroy of resource groups.

Since we cannot guarantee our testing environment won’t be applied some Azure Policy Remediation Tasks in the future, for a robust testing environment, prevent_deletion_if_contains_resources SHOULD be explicitly set to false.




See origin...

ID: TFNFR37 - Category: Code Style - Tool Usage by Module Owner

newres is a command-line tool that generates Terraform configuration files for a specified resource type. It automates the process of creating variables.tf and main.tf files, making it easier to get started with Terraform and reducing the time spent on manual configuration.

Module owners MAY use newres when they’re trying to add new resource block, attribute, or nested block. They MAY generate the whole block along with the corresponding variable blocks in an empty folder, then copy-paste the parts they need with essential refactoring.




Inputs / Outputs

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR14Data TypesSHOULDOwnerContributorBAU
2SNFR22Parameters/Variables for Resource IDsMUSTOwnerContributorBAU
3SNFR26Output - Parameters - DecoratorsMUSTOwnerContributorBAU
4RMFR6Parameter/Variable NamingMUSTOwnerContributorBAU
5RMFR7Minimum Required OutputsMUSTOwnerContributorBAU
6RMNFR2Parameter/Variable NamingMUSTOwnerContributorBAU
7TFFR2Additional Terraform OutputsSHOULDOwnerContributorBAU
8TFNFR14Not allowed variablesMUSTOwnerContributorBAU
➕ See Specifications for this category
See origin...

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.




See origin...

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}, SHOULD contain ResourceId/resource_id in its parameter/variable name when that parameter/variable is part of a user-defined type. This assists 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 a user-defined type: in Bicep its parameter name should be workspaceResourceId and the variable name in Terraform should be workspace_resource_id.

In that user-defined context, workspaceId is not descriptive enough and is ambiguous as to which ID is required to be input.

Special considerations for Bicep

If the property is nested in a parameter and you opt for a resource-derived type (that is, a schema defined by the resource provider), this requirement does not apply. We do however recommend to use a user-defined type whenever these cases occur to increase the module’s usability.

Example for the property subnetArmId of the Cognitive Service’s property networkInjections:

If using a user-defined type, you may define a type for the networkInjections parameter like

param networkInjections networkInjectionType?

@export()
type networkInjectionType = {
  subnetResourceId: string

  // (...)
}

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: [{
      subnetArmId: networkInjections.?subnetResourceId
      // (...)
    }]
  }
}

or a resource-derived type like

param networkInjections resourceInput<'Microsoft.CognitiveServices/accounts@2025-06-01'>.properties.networkInjections

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' = {
  // (...)
  properties: {
    // (...)
    networkInjections: networkInjections
  }
}



See origin...

ID: SNFR26 - Output-Parameters - Decorators

Output parameters MUST implement:

Output parameters
@description('The resourceId of your resource.')
output sampleResourceId string = sampleResource.id

@description('The key of your resource.')
@secure()
output sampleResourceKey string = sampleResource.key
# Resource output
output "foo" {
  description = "MyResource foo attribute"
  value = azurerm_resource_myresource.foo
}

# Output of a sensitive attribute
output "bar" {
  description = "MyResource bar attribute"
  value     = azurerm_resource_myresource.bar
  sensitive = true
}



See origin...

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.




See origin...

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
Tip

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




See origin...

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)



See origin...

ID: TFFR2 - Category: Outputs - Additional Terraform Outputs

Authors SHOULD NOT output entire resource objects as these may contain sensitive outputs and the schema can change with API or provider versions.
Instead, authors SHOULD output the computed attributes of the resource as discreet outputs.
This kind of pattern protects against provider schema changes and is known as an anti-corruption layer.

Remember, you SHOULD NOT output values that are already inputs (other than name).

E.g.,

# Resource output, computed attribute.
output "foo" {
  description = "MyResource foo attribute"
  value = azurerm_resource_myresource.foo
}

# Resource output for resources that are deployed using `for_each`. Again only computed attributes.
output "childresource_foos" {
  description = "MyResource children's foo attributes"
  value = {
    for key, value in azurerm_resource_mychildresource : key => value.foo
  }
}

# Output of a sensitive attribute
output "bar" {
  description = "MyResource bar attribute"
  value     = azurerm_resource_myresource.bar
  sensitive = true
}



See origin...

ID: TFNFR14 - Category: Inputs - Not allowed variables

Since Terraform 0.13, count, for_each and depends_on are introduced for modules, module development is significantly simplified. Module’s owners MUST NOT add variables like enabled or module_depends_on to control the entire module’s operation. Boolean feature toggles are acceptable however.




Testing

The content below is listed based on the following tags
#IDTitleSeverityPersonaLifecycle
1SNFR1Prescribed TestsMUSTOwnerContributorBAU
2SNFR2E2E TestingMUSTOwnerContributorBAU
3SNFR3AVM Compliance TestsMUSTOwnerContributorInitial
4SNFR4Unit TestsSHOULDOwnerContributorBAU
5SNFR5Upgrade TestsSHOULDOwnerContributorBAU
6SNFR6Static Analysis/Linting TestsMUSTOwnerContributorBAU
7SNFR7Idempotency TestsMUSTOwnerContributorBAU
8SNFR24Testing Child, Extension & Interface ResourcesMUSTOwnerContributorBAU
9TFNFR5Test ToolingMUSTOwnerContributorBAU
10TFNFR15Variable Definition OrderSHOULDOwnerContributorBAU
➕ See Specifications for this category
See origin...

ID: SNFR1 - Category: Testing - Prescribed Tests

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




See origin...

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.

Tip

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

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 & Bicep Log Analytics Workspace examples using simple/native declarations for use in E2E tests

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