Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

☁️ Azure Virtual Network Dns Servers Terraform Module

Manage the custom DNS server list of an existing Azure virtual network as its own resource, separate from the virtual network itself. Targets hashicorp/azurerm ~> 4.0.

Terraform azurerm Module Type Resources


🧩 Overview

  • 🌐 Sets the ordered custom DNS server list on an existing virtual network via azurerm_virtual_network_dns_servers.
  • 🧭 Points a network's name resolution at a private-resolution data plane β€” a Private DNS Resolver inbound endpoint, a hub DNS forwarder, or custom DNS hosts β€” so private endpoints and on-premises names resolve for every workload in the network.
  • 🧱 Manages the DNS list on a dedicated resource, decoupled from the virtual network resource, so a DNS/platform team changes resolution without re-planning the network the network team owns.
  • πŸ”’ Secure by default β€” the empty call sets no custom servers and leaves the network on Azure-provided default DNS; a custom path is an explicit, reviewable opt-in.
  • 🧰 Emits the assignment id, the target virtual_network_id, and the effective dns_servers for downstream wiring.

πŸ’‘ Why it matters: In a hub-and-spoke or private-endpoint topology, the virtual network's DNS setting is what makes private zones and hybrid names resolvable. Owning that one setting on its own resource keeps a high-churn, cross-team knob out of the virtual network's lifecycle, so a resolver change never forces a virtual-network plan.

❀️ Support this project

If this module saves you time, please consider supporting its continued development:

πŸ—ΊοΈ Where this fits in the family

graph LR
  rg["terraform-azurerm-resource-group"]
  vnet["terraform-azurerm-virtual-network"]
  resolver["terraform-azurerm-<br/>private-dns-resolver-inbound-endpoint"]
  this["terraform-azurerm-virtual-network-dns-servers"]
  target["azurerm_virtual_network_dns_servers.this"]

  rg -->|"resource_group_name"| vnet
  vnet -->|"virtual_network_id"| this
  resolver -->|"private_ip_address to dns_servers"| this
  this -->|"manages"| target

  classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
  classDef keystone fill:#004578,stroke:#004578,color:#ffffff;
  classDef sibling fill:#eef3f8,stroke:#b8c4d0,color:#1b2733;

  class this me;
  class target keystone;
  class rg,vnet,resolver sibling;
Loading

This module sits downstream of the virtual network it configures. It consumes a virtual_network_id from terraform-azurerm-virtual-network and a set of DNS server addresses β€” typically a Private DNS Resolver inbound endpoint IP, a hub forwarder, or custom DNS hosts β€” and manages only the network's DNS server list.

🧬 What this module builds

graph LR
  vnid["virtual_network_id (required, force-new)"]
  dns["dns_servers (list, default empty)"]
  timeouts["timeouts (optional)"]
  this["azurerm_virtual_network_dns_servers.this"]
  oid["output: id"]
  ovn["output: virtual_network_id"]
  odns["output: dns_servers"]

  vnid -->|"targets VNet"| this
  dns -->|"custom DNS list"| this
  timeouts -->|"operation timeouts"| this
  this -->|"emits"| oid
  this -->|"emits"| ovn
  this -->|"emits"| odns

  classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
  classDef keystone fill:#004578,stroke:#004578,color:#ffffff;
  classDef sibling fill:#eef3f8,stroke:#b8c4d0,color:#1b2733;

  class this keystone;
  class vnid,dns,timeouts,oid,ovn,odns sibling;
Loading

Resource inventory

Resource Count Role
azurerm_virtual_network_dns_servers.this 1 The keystone; sets the custom DNS server list on the referenced virtual network.

There are no child collections β€” this is a single-resource standalone module.

βœ… Provider / Versions

Requirement Value
Terraform >= 1.12.0
azurerm provider ~> 4.0
Provider block None in this module β€” the caller configures provider "azurerm" { features {} }, auth, and subscription.

Schema notes that bite (verified against the live provider schema):

  • virtual_network_id is force-new / immutable β€” changing it destroys and recreates the DNS-server assignment.
  • This resource and an inline dns_servers argument on azurerm_virtual_network manage the same underlying setting. Managing both against one network produces a perpetual diff; use exactly one.
  • The resource has no name, location, resource_group_name, or tags β€” those are all inherited from the referenced virtual network β€” so this module intentionally exposes none of them.
  • Setting dns_servers to an empty list reverts the network to Azure-provided default DNS (168.63.129.16); it does not delete the network.

πŸ”‘ Required Azure RBAC Roles / Permissions

  • Network Contributor on the resource group (or the virtual network scope) that contains the target virtual network, or a custom role granting Microsoft.Network/virtualNetworks/read and Microsoft.Network/virtualNetworks/write at that scope. Updating the DNS server list is a write on the parent virtual network. Least-privilege: scope the assignment to the single virtual network where practical.

Azure Prerequisites

  • The Microsoft.Network resource provider registered on the target subscription.
  • The target virtual network already exists; its resource ID is the virtual_network_id input.
  • Any DNS endpoint the list points at β€” a Private DNS Resolver inbound endpoint, a hub DNS forwarder, or custom DNS virtual machines β€” is already provisioned and reachable from the network's address space.
  • The caller configures provider "azurerm" { features {} }, auth, and subscription; the module declares none of these.

πŸ“ Module Structure

terraform-azurerm-virtual-network-dns-servers/
β”œβ”€β”€ providers.tf   # required_version >= 1.12.0; azurerm ~> 4.0; no provider block
β”œβ”€β”€ variables.tf   # virtual_network_id, dns_servers (shape-checked, not IPv4), timeouts
β”œβ”€β”€ main.tf        # azurerm_virtual_network_dns_servers.this + dynamic timeouts
β”œβ”€β”€ outputs.tf     # id, virtual_network_id, dns_servers
β”œβ”€β”€ README.md      # this document
β”œβ”€β”€ SCOPE.md       # cross-module contract
β”œβ”€β”€ LICENSE        # MIT
└── .gitignore     # canonical library ignore set

βš™οΈ Quick Start

provider "azurerm" {
  features {}
}

module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-network/providers/Microsoft.Network/virtualNetworks/vnet-hub"
  dns_servers        = ["10.0.0.4"]
}

ℹ️ The caller configures the provider, authentication, and the mandatory features {} block β€” this module declares none of them. Always pin ?ref=v1.0.0, never a branch.

πŸ”Œ Cross-Module Contract

Consumes

Input Type Source
virtual_network_id string terraform-azurerm-virtual-network (id)
dns_servers list(string) Private DNS Resolver inbound endpoint IP, hub DNS forwarder, or custom DNS host addresses

Emits

Output Description Consumed by
id The assignment Resource ID downstream references, documentation
virtual_network_id Resource ID of the configured virtual network compositions wiring the same network elsewhere
dns_servers Effective ordered custom DNS server list (empty = Azure default DNS) validation, documentation, IPAM

πŸ“š Example Library

1 Β· Minimal call β€” single custom DNS server
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.vnet_id
  dns_servers        = ["10.0.0.4"]
}

πŸ’‘ The smallest useful call: point the network at one custom DNS server. Order matters β€” the first reachable server answers.

2 Β· Point a network at a Private DNS Resolver inbound endpoint
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.spoke_vnet_id
  dns_servers        = [var.resolver_inbound_endpoint_ip] # e.g. "10.10.0.4"
}

πŸ’‘ A Private DNS Resolver inbound endpoint is the recommended target for private-endpoint and hybrid resolution β€” it replaces custom DNS virtual machines with a managed service.

3 Β· Dual custom DNS servers (primary + secondary)
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.vnet_id
  dns_servers        = ["10.0.0.4", "10.0.0.5"] # primary, then secondary
}

ℹ️ List order is significant. Clients try servers in order, so place the most reliable resolver first.

4 Β· Revert to Azure-provided default DNS (empty list)
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.vnet_id
  dns_servers        = [] # the default β€” hands resolution back to Azure default DNS (168.63.129.16)
}

πŸ”’ The empty list is the secure default. It resolves public names and same-network private-DNS-zone links, but not custom or on-premises zones. Setting the list back to empty reverts the network to Azure default DNS without deleting it.

5 Β· Hub-and-spoke DNS pattern β€” spoke points at the hub forwarder
module "spoke_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.spoke_vnet_id
  dns_servers        = [var.hub_dns_forwarder_ip] # centralize resolution in the hub
}

πŸ’‘ In hub-and-spoke, every spoke points its DNS at the hub's forwarder (a resolver inbound endpoint or DNS host). Conditional forwarding and on-premises resolution then live in one place.

6 Β· Custom DNS virtual machines (self-managed resolvers)
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.vnet_id
  dns_servers        = ["10.20.1.10", "10.20.1.11"] # DNS forwarder VMs in a shared subnet
}

⚠️ Self-managed DNS VMs must stay reachable from the network's address space. Prefer a Private DNS Resolver where possible to remove the VM maintenance burden.

7 Β· Why a separate resource β€” ownership boundary
# The network team owns the virtual network (terraform-azurerm-virtual-network) and does NOT set dns_servers
# inline. The DNS/platform team owns resolution through this module against the same network id.
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.shared_vnet_id
  dns_servers        = [var.resolver_inbound_endpoint_ip]
}

⚠️ Manage the DNS list here or inline on the virtual network, never both β€” co-managing the same setting from two resources produces a perpetual diff. Splitting it out keeps a resolver change from forcing a virtual-network plan.

8 Β· Ordered failover β€” three resolvers
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.vnet_id
  dns_servers        = ["10.0.0.4", "10.0.1.4", "10.0.2.4"] # zone-spread resolvers, tried in order
}

ℹ️ Spread resolvers across zones and list them in preference order for resilient resolution.

9 Β· Manage DNS for many networks with for_each
variable "network_dns" {
  type = map(object({
    virtual_network_id = string
    dns_servers        = list(string)
  }))
}

module "vnet_dns" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"
  for_each = var.network_dns

  virtual_network_id = each.value.virtual_network_id
  dns_servers        = each.value.dns_servers
}

πŸ’‘ A stable map key per network keeps for_each from re-indexing when you add or remove one network.

10 Β· Explicit operation timeouts
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.vnet_id
  dns_servers        = ["10.0.0.4"]

  timeouts = {
    create = "30m"
    update = "30m"
    delete = "30m"
  }
}

ℹ️ Timeouts are the only universal-tail input this resource supports; tags is intentionally absent.

11 Β· Wire the virtual-network module output directly
module "vnet" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network.git?ref=v1.0.0"

  name                = "vnet-hub"
  resource_group_name = "rg-network-hub"
  location            = "eastus"
  address_space       = ["10.20.0.0/16"]
}

module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = module.vnet.id
  dns_servers        = ["10.0.0.4"]
}

πŸ’‘ Consume the sibling network module's id output rather than hardcoding a resource ID string.

12 Β· Hybrid resolution β€” on-premises DNS plus a resolver
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.vnet_id
  dns_servers = [
    var.resolver_inbound_endpoint_ip, # Azure private zones + conditional forwarding
    var.onprem_dns_primary_ip,        # reachable over VPN/ExpressRoute
  ]
}

⚠️ On-premises servers must be reachable across the connected VPN or ExpressRoute path, and Azure private zones are best served by a resolver inbound endpoint rather than an on-premises server.

13 Β· Enabling private-endpoint name resolution
# Private endpoints publish A records into a private DNS zone. Workloads only resolve them when the network's
# DNS points at a resolver/forwarder that can answer for that zone.
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = var.app_vnet_id
  dns_servers        = [var.resolver_inbound_endpoint_ip]
}

πŸ”’ Custom DNS is what turns a private endpoint into a resolvable, private-only name for the workloads in the network β€” a core building block of a no-public-exposure data plane.

14 Β· πŸ—οΈ End-to-end composition
provider "azurerm" {
  features {}
}

module "rg" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
  name     = "rg-network-prod"
  location = "eastus2"
}

module "vnet" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network.git?ref=v1.0.0"
  name                = "vnet-hub-prod"
  resource_group_name = module.rg.name
  location            = module.rg.location
  address_space       = ["10.0.0.0/16"]

  subnets = {
    resolver_inbound = {
      name             = "snet-resolver-inbound"
      address_prefixes = ["10.0.0.0/28"]
    }
  }
}

module "resolver" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-private-dns-resolver.git?ref=v1.0.0"
  name                = "pdr-hub-prod"
  resource_group_name = module.rg.name
  location            = module.rg.location
  virtual_network_id  = module.vnet.id
}

# Inbound endpoints are a SEPARATE resource, and therefore a separate module.
module "resolver_inbound" {
  source                  = "git::https://github.com/microsoftexpert/terraform-azurerm-private-dns-resolver-inbound-endpoint.git?ref=v1.0.0"
  name                    = "pdr-hub-prod-inbound"
  private_dns_resolver_id = module.resolver.id
  location                = "eastus"

  ip_configurations = [{
    subnet_id = module.vnet.subnet_ids["resolver_inbound"]
  }]
}

# This module sets the hub network's DNS to the resolver's inbound endpoint IP.
module "vnet_dns" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network-dns-servers.git?ref=v1.0.0"

  virtual_network_id = module.vnet.id
  dns_servers        = [module.resolver_inbound.private_ip_address]
}

πŸ’‘ The composition wires real sibling outputs: the resource group feeds the virtual network, the virtual network feeds both the resolver and this module, and the inbound endpoint module's private_ip_address becomes the network's DNS server. Keeping the DNS list on its own resource means changing the resolver never re-plans the virtual network.

πŸ“₯ Inputs

Primary identity

Name Type Required Description
virtual_network_id string βœ… Resource ID of the existing virtual network whose DNS list is managed. Force-new.

Configuration

Name Type Default Description
dns_servers list(string) [] Ordered custom DNS server IPv4 addresses; empty means Azure default DNS.

Universal tail

Name Type Default Description
timeouts object({...}) null Optional create/read/update/delete timeouts.

ℹ️ This resource does not support tags, name, location, or resource_group_name; the module exposes none of them.

Full object() schemas
variable "virtual_network_id" {
  type = string
  # Full VNet resource ID:
  #   /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Network/virtualNetworks/<vnet>
  # Immutable: changing it forces replacement of the DNS-server assignment.
}

variable "dns_servers" {
  type    = list(string)
  default = []
  # Entries are checked for shape, not for IPv4 -- Azure supports dual-stack virtual networks and
  # the provider itself requires only a non-empty string. Empty list = Azure-provided default DNS.
}

variable "timeouts" {
  type = object({
    create = optional(string)
    read   = optional(string)
    update = optional(string)
    delete = optional(string)
  })
  default = null
}

🧾 Outputs

Output Description Kind
id Resource ID of the virtual network DNS-servers assignment (emitted first) Passthrough
virtual_network_id Resource ID of the virtual network whose DNS server list this module manages Passthrough
virtual_network_name Name of the virtual network being configured, parsed from virtual_network_id Derived
resource_group_name Resource group the assignment lives in, parsed from virtual_network_id Derived
subscription_id Subscription the virtual network lives in, parsed from virtual_network_id Derived
dns_servers The effective ordered list of custom DNS server addresses set on the virtual network, as the provider reads it back Passthrough
dns_server_count Number of custom DNS servers configured, against the fixed Azure ceiling of 20 per virtual network Passthrough
primary_dns_server The first entry in the list, or null when there is none Derived
uses_azure_provided_dns True when no custom DNS server is configured and the network therefore resolves through Azure-provided DNS at 168.63.129.16 Passthrough
has_a_single_point_of_dns_failure True when exactly one custom DNS server is configured Passthrough
duplicate_dns_servers Addresses that appear more than once in the list Derived
entries_that_are_not_ipv4_shaped Entries that do not look like a dotted-quad IPv4 address Derived
an_empty_list_means_azure_provided_dns_not_no_dns Always true, and the single most misread thing about this resource Constant
destroying_this_resource_reverts_the_network_to_azure_provided_dns Always true Constant
creating_this_resource_silently_replaces_any_existing_dns_server_list Always true, and the reason to check a network's current DNS before pointing this module at it Constant
is_a_singleton_per_virtual_network Always true Constant
an_inline_dns_servers_argument_on_the_virtual_network_would_fight_this_resource Always true Constant
every_change_rewrites_the_whole_virtual_network_object Always true, and the reason this resource is more disruptive than its two arguments suggest Constant
a_change_takes_effect_only_after_a_lease_renewal_or_restart Always true, and the most common reason a correct apply appears to do nothing Constant
resolution_order_is_significant_and_there_is_no_round_robin Always true Constant
a_reachable_but_broken_first_resolver_is_never_failed_over_from Always true, and the failure mode a second entry in the list does NOT protect against Constant
custom_dns_makes_port_53_traffic_bypass_network_security_groups Always true once any custom DNS server is set, and it is a security control changing behaviour with no corresponding change in the plan Constant
network_interface_dns_settings_override_this_list Always true Constant
azure_private_dns_zones_need_a_resolver_or_forwarder_in_the_list Always true, and the reason a private endpoint stops resolving the moment custom DNS is set Constant
windows_forwarders_need_a_timeout_above_four_seconds Always true where the servers named here are Windows DNS servers forwarding to Azure Constant
virtual_network_id_is_force_new Always true Constant
resource_ids_are_parsed_case_sensitively Always true Constant
the_resource_has_no_tags_name_location_or_resource_group Always true Constant
this_module_creates_no_resolver_forwarder_or_zone Always true Constant
accepts_no_credential Always true Constant

🧠 Architecture Notes

  • Single-resource standalone. main.tf renders exactly one resource, azurerm_virtual_network_dns_servers.this, plus a dynamic "timeouts" block that renders only when the timeouts object is supplied. Every optional nested field uses try(..., null) so an omitted key renders as absent, not as an error.
  • Force-new identity. virtual_network_id is immutable. A change destroys and recreates the assignment; because the assignment reverts the network to default DNS on delete, plan such a change during a maintenance window.
  • Do not double-manage. The dns_servers argument on azurerm_virtual_network and this resource write the same setting. Pick one owner. This module deliberately references the network by id and never mutates the network resource, keeping ownership boundaries clean.
  • Empty means default, not deletion. An empty dns_servers list is a valid, safe state that hands resolution back to Azure default DNS. It is the module's default so the empty call is safe.
  • No tags. Unlike most resources in this suite, azurerm_virtual_network_dns_servers supports no tags, name, location, or resource group of its own β€” all are inherited from the referenced network β€” so the universal tail here is timeouts only.
  • features {} dependence. The provider will not initialize without a caller-side provider "azurerm" { features {} } block; that belongs to the root module.

🧱 Design Principles

Concern Secure default (empty call) Opt-out (caller types it)
Custom DNS override dns_servers = [] β†’ Azure-provided default DNS supply one or more server IPs
Resolution path No custom private-resolution path until explicitly wired point at a resolver / forwarder / DNS host
Ownership DNS list owned on its own resource, network referenced by id (not applicable β€” the module never co-manages the network)
Secrets None accepted or emitted (not applicable)

πŸš€ Runbook

# From the module folder β€” offline, no cloud calls:
terraform init -backend=false
terraform validate
terraform fmt -check
  • Pin the module with ?ref=v1.0.0 β€” never a branch.
  • This library is plan-only during authoring; a human runs terraform plan / apply from CI against real credentials. No apply happens here.

πŸ§ͺ Testing

The offline proof gate covers everything static analysis can prove:

  • terraform init -backend=false resolves the pinned azurerm ~> 4.0 provider without a backend.
  • terraform validate proves the configuration is internally consistent and type-correct against the pinned provider schema β€” it surfaces every typing mistake the object() schemas and the IPv4 validation {} block are designed to catch.
  • terraform fmt -check enforces canonical formatting.

What only terraform plan (run by a human from CI) exercises: the ARM write against the live virtual network and the actual DNS-server assignment. This module is never applied during authoring.

πŸ’¬ Example Output

Outputs:

id                 = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-network-prod/providers/Microsoft.Network/virtualNetworks/vnet-hub-prod/dnsServers/default"
virtual_network_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-network-prod/providers/Microsoft.Network/virtualNetworks/vnet-hub-prod"
dns_servers        = [
  "10.0.0.4",
]

πŸ” Troubleshooting

Symptom Cause Fix
Perpetual diff on the DNS server list The same network sets dns_servers inline on azurerm_virtual_network and via this module. Remove the inline dns_servers from the network resource; let this module own it (or vice versa) β€” never both.
Plan shows the assignment being replaced virtual_network_id changed, and it is force-new. Confirm the intended network ID; expect recreation and schedule it during a maintenance window.
virtual_network_id must be a virtual network Resource ID of the form ... A name or partial path was passed instead of a full Resource ID β€” or a segment was miscased, which the provider parses case-sensitively. Pass the complete /subscriptions/.../virtualNetworks/<name> ID, e.g. module.vnet.id, with resourceGroups and Microsoft.Network spelled exactly.
A dns_servers entry is rejected The entry carries a CIDR suffix, a comma, whitespace, or looks like a hostname. IPv4 is not required β€” an IPv6 resolver is legal on a dual-stack network. Supply one bare IP address per entry (for example 10.0.0.4).
Private endpoints or on-premises names still do not resolve The network points at Azure default DNS, or the target resolver cannot answer for the zone. Set dns_servers to a resolver inbound endpoint / forwarder that holds the private zone or conditional-forwarding rules.
Error: provider ... features on init/plan No caller-side features {} block. Add provider "azurerm" { features {} } to the root module.

πŸ”— Related Docs

πŸ’™ "Infrastructure as Code should be standardized, consistent, and secure."