Let me start with saying that I'm an absolute Terraform and IaC in general newbie, so whatever it described here might not be the best approach to things. Doing a lot of stuff based on different Terraform codes we have already in the organization, without doing much trainings/courses/etc. so a lot of self-learn approach is used.
What I'm attempting to do is create a generic Terraform module that I could use across different other modules within Azure. This is what I currently have:
data "azurerm_private_dns_zone" "dnszone" {
provider = azurerm.csvc
name = var.pdns_name
resource_group_name = var.pdns_rg_name
}
resource "azurerm_private_endpoint" "private_endpoint" {
for_each = [var.subresource_names_map]
name = format("%s-%s-%s", "pe", var.private_connection_resource_name, each.value)
location = var.location
resource_group_name = var.pe_resource_group_name
subnet_id = var.private_endpoint_subnet_id
tags = merge({ StartDate = formatdate("DD-MM-YYYY", time_static.time.rfc3339) }, var.env_tags)
private_service_connection {
name = format("%s-%s-%s", var.private_connection_resource_name, each.value, "Conn")
private_connection_resource_id = var.private_connection_resource_id
is_manual_connection = false
subresource_names = [each.value]
}
private_dns_zone_group {
name = var.pdns_rg_name
private_dns_zone_ids = [data.azurerm_private_dns_zone.dnszone.id]
}
}
And I believe this is generally going to work for two scenarios:
pdns_name = "privatelink.sql.azuresynapse.net"
subresourece_names_map = ["Sql", "SqlOnDemand"]
What I'm thinking of doing is is solving a third scenario - one resource can have multiple subresources go into multiple private DNS zone (ex. Storage Account having overall 5 different subresources going into 5 different PDNSZ).
This would require having some sort of dictionary that would map subresources into different PDNSZ, however I think there are cases where a single subresource can be mapped into different PDNSZ (I think both Synapse and PostgreSQL use Sql as subresource, few resources use portal etc.). Right now I could achieve this by simply referencing PE module more than once and this is probably the safest, but not sure whether it's the most optimal code wise.
data "azurerm_client_config" "current" {}
resource "time_static" "time" {}
resource "azurerm_key_vault" "kv" {
name = var.name
location = var.location
resource_group_name = var.resource_group_name
sku_name = var.sku_name
enabled_for_disk_encryption = true
tenant_id = data.azurerm_client_config.current.tenant_id
soft_delete_retention_days = var.soft_delete_retention_days
purge_protection_enabled = true
enable_rbac_authorization = var.enable_rbac_authorization
public_network_access_enabled = var.public_network_access_enabled
tags = merge({ StartDate = formatdate("DD-MM-YYYY", time_static.time.rfc3339) }, var.env_tags)
lifecycle {
ignore_changes = [tags]
}
}
module "private_endpoint" {
source = "../private_endpoint"
pdns_rg_name = var.pdns_rg_name
pdns_name = var.pdns_name
pe_resource_group_name = var.pe_resource_group_name
location = var.location
private_endpoint_subnet_id = var.private_endpoint_subnet_id
private_connection_resource_id = azurerm_key_vault.kv.id
private_connection_resource_name = azurerm_key_vault.kv.name
subresource_names_map = var.subresource_names_map
env_tags = var.env_tags
}
Azure Private Endpoint with Terraform - smart way to do mapping between Subresource and Private DNS Zone
We need to make 3 main changes in the code configuration i.e., the way we map the resources by use of for_loop over this map and we also need to make changes according to code shared as mentioned below in the module private_dns_zone_group .
configuration:
module/private_end_point/main.tf:
resource "azurerm_private_dns_zone" "dnszones" {
for_each = { for dns in flatten([for dns in values(var.subresource_dns_map) : dns]) : dns => { name = dns, rg_name = var.pdns_rg_name } }
name = each.value.name
resource_group_name = each.value.rg_name
}
resource "azurerm_private_endpoint" "private_endpoint" {
for_each = var.subresource_dns_map
name = format("%s-%s-%s", "pe", var.private_connection_resource_name, each.key)
location = var.location
resource_group_name = var.pe_resource_group_name
subnet_id = var.private_endpoint_subnet_id
tags = merge({ StartDate = formatdate("DD-MM-YYYY", timestamp()) }, var.env_tags)
private_service_connection {
name = format("%s-%s-%s", var.private_connection_resource_name, each.key, "Conn")
private_connection_resource_id = var.private_connection_resource_id
is_manual_connection = false
subresource_names = [each.key]
}
dynamic "private_dns_zone_group" {
for_each = [for dns_zone in each.value : {
name = dns_zone
id = azurerm_private_dns_zone.dnszones[dns_zone].id
}]
content {
name = private_dns_zone_group.value.name
private_dns_zone_ids = [private_dns_zone_group.value.id]
}
}
}
main.tf:
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "example_rg" {
name = var.pdns_rg_name
location = var.location
}
resource "azurerm_virtual_network" "example_vnet" {
name = "vksb-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example_rg.location
resource_group_name = azurerm_resource_group.example_rg.name
}
resource "azurerm_subnet" "example_subnet" {
name = "vksb-subnet"
resource_group_name = azurerm_resource_group.example_rg.name
virtual_network_name = azurerm_virtual_network.example_vnet.name
address_prefixes = ["10.0.1.0/24"]
}
resource "azurerm_storage_account" "example_storage" {
name = "vksbstorageacct"
resource_group_name = azurerm_resource_group.example_rg.name
location = azurerm_resource_group.example_rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}
module "private_endpoint_example" {
source = "./modules/private_endpoint"
pdns_rg_name = azurerm_resource_group.example_rg.name
pe_resource_group_name = azurerm_resource_group.example_rg.name
location = azurerm_resource_group.example_rg.location
private_endpoint_subnet_id = azurerm_subnet.example_subnet.id
private_connection_resource_id = azurerm_storage_account.example_storage.id
private_connection_resource_name = azurerm_storage_account.example_storage.name
subresource_dns_map = {
"blob" = ["privatelink.blob.core.windows.net"]
"file" = ["privatelink.file.core.windows.net"]
}
env_tags = {
Environment = "dev"
Project = "example"
}
}
deployment:
For more Info related to DNS zones provision using terraform you can refer to answer provided by me in SO link.
Refer:
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_zone
Azure Private Endpoint & DNS zones using Terraform | by Kirupakarans | Medium by kirupakarans