How to delegate Azure subnets to Azure services using for_each and avoiding PostgreSQL Flexible Server is missing required delegations “Microsoft.DBforPostgreSQL/flexibleServers”.
Error
│ Error: waiting for creation of the Postgresql Flexible Server "example" (Resource Group "vexample-resources-test"): Code="OperationFailed" Message="The subnet name as example-app-dev is missing required delegations Microsoft.DBforPostgreSQL/flexibleServers"
│
│ with azurerm_postgresql_flexible_server.postgreSQL,
│ on postgres.tf line 38, in resource "azurerm_postgresql_flexible_server" "postgreSQL":
│ 38: resource "azurerm_postgresql_flexible_server" "postgreSQL" {
A subnet delegation within Azure allows you to designate a specific subnet for an Azure resource. This means that no other resource types can be in the delegated subnet. When you delegate a subnet to an Azure resource, you allow that service to establish some basic network configuration rules for that subnet, which allows the Azure resource operate the instances in a stable manner. You can delegate a subnet by assigning the delegation property service_delegation
within the resource block of that subnet.
Example
resource "azurerm_subnet" "example" {
name = "example-sn"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.2.0/24"]
service_endpoints = ["Microsoft.Storage"]
delegation {
name = "fs"
service_delegation {
name = "Microsoft.DBforPostgreSQL/flexibleServers"
actions = [
"Microsoft.Network/virtualNetworks/subnets/join/action",
]
}
}
}
There are certain situations that require multiple subnets to be created yet only one of them needs to be a designated subnet. Using for_each
along with a dynamic block will allow Terraform to create multiple subnets on the virtual network while delegating one of them to a service. In this example, azurerm_postgresql_flexible_server
requires a service_delegation
while other subnets do not. Using Terraform, create a variable of type map(object) with the name, address, and service delegation using the example below:
variable "subnetPrefix" {
type = map(object({
name = string
address = list(string)
service_delegation = bool
}))
}
Next, add the following block to the terraform.tfvars file, if present, setting the service delegation to true
for the appropriate subnet.
subnetPrefix = {
"s1" = { name = "example-web-dev", address = ["172.31.6.0/28"], service_delegation = false },
"s2" = { name = "example-db-dev", address = ["172.31.6.16/28"], service_delegation = true },
"s3" = { name = "example-app-dev", address = ["172.31.6.32/27"], service_delegation = false }
}
Terraform resource: Configure the Terraform resource using for_each
along with a conditional which will evaluate the service_delegation
boolean and if true
Terraform will assign the service delegation to this network.
resource "azurerm_subnet" "example" {
for_each = var.subnetPrefix
name = each.value.name
address_prefixes = each.value.address
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
enforce_private_link_endpoint_network_policies = true
dynamic "delegation" {
for_each = each.value.service_delegation == true ? [1] : []
content {
name = "delegation"
service_delegation {
name = "Microsoft.DBforPostgreSQL/flexibleServers"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action", ]
}
}
}
}
Terraform will perform the following actions:
# azurerm_subnet.example["s1"] will be created
+ resource "azurerm_subnet" "example[0]" {
+ address_prefixes = [
+ "172.31.6.0/28",
]
+ enforce_private_link_endpoint_network_policies = true
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "example-web-dev"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "example-resources-test"
+ virtual_network_name = "example-network"
}
# azurerm_subnet.example["s2"] will be created
+ resource "azurerm_subnet" "example[1]" {
+ address_prefixes = [
+ "172.31.6.16/28",
]
+ enforce_private_link_endpoint_network_policies = true
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "example-db-dev"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "example-resources-test"
+ virtual_network_name = "example-network"
+ delegation {
+ name = "delegation"
+ service_delegation {
+ actions = [
+ "Microsoft.Network/virtualNetworks/subnets/join/action",
]
+ name = "Microsoft.DBforPostgreSQL/flexibleServers"
}
}
}
# azurerm_subnet.example["s3"] will be created
+ resource "azurerm_subnet" "example[2]" {
+ address_prefixes = [
+ "172.31.6.32/27",
]
+ enforce_private_link_endpoint_network_policies = true
+ enforce_private_link_service_network_policies = (known after apply)
+ id = (known after apply)
+ name = "example-app-dev"
+ private_endpoint_network_policies_enabled = (known after apply)
+ private_link_service_network_policies_enabled = (known after apply)
+ resource_group_name = "example-resources-test"
+ virtual_network_name = "example-network"
}
Plan: 3 to add, 0 to change, 0 to destroy.
Results
In the above plan, azurerm_subnet.example["s2"]
contains a delegation sub-block while the other two subnets do not.