
Deploying Azure PostgreSQL Flexible Server with Terraform/OpenTofu — Private DNS, Subnet Delegation, and Lifecycle Control
- Posted by Martin Linxfeld
- Categories Azure Cloud, Databases, opentofu, storage, terraform
- Date October 27, 2025
- Comments 0 comment
- Tags Azure Database, Azure PostgreSQL, Infrastructure as Code, Managed Database, Multicloud, OpenTofu, Private DNS, Subnet Delegation, terraform
In this post, we’ll explore how to deploy Azure PostgreSQL Terraform, using Private DNS, subnet delegation, and full lifecycle control.
Databases are the heart of every cloud deployment. While compute instances can spin up and down in seconds, your data layer demands stability, privacy, and precision. In Azure, that’s where PostgreSQL Flexible Server comes in — a fully managed database service that you can provision, secure, and connect privately using Terraform or OpenTofu. Let’s walk through a real example straight from my Multicloud Foundations training.
Step 1 — Deploying Azure PostgreSQL Terraform
Here’s a complete Terraform definition that creates a managed PostgreSQL instance in Azure.
You’ll notice a few important arguments that go beyond the basics:
resource "azurerm_postgresql_flexible_server" "foggykitchen_pg" {
name = "foggykitchen-pg"
resource_group_name = azurerm_resource_group.foggykitchen_rg.name
location = azurerm_resource_group.foggykitchen_rg.location
version = "13"
administrator_login = var.pg_admin_username
administrator_password = var.pg_admin_password
storage_mb = 32768
sku_name = "GP_Standard_D2s_v3"
delegated_subnet_id = azurerm_subnet.foggykitchen_db_subnet.id
private_dns_zone_id = azurerm_private_dns_zone.foggykitchen_pg_dns.id
public_network_access_enabled = false
maintenance_window {
day_of_week = 0
start_hour = 22
start_minute = 0
}
lifecycle {
ignore_changes = [
zone
]
}
depends_on = [
azurerm_private_dns_zone_virtual_network_link.foggykitchen_pg_dns_link
]
}
💡 Let’s break down the most interesting parts:
delegated_subnet_id– the database runs in a delegated subnet, meaning it has no public IP and relies entirely on your VNet.private_dns_zone_id– this links the server to a private DNS zone, so applications can connect viafoggykitchen-pg.postgres.database.azure.cominternally.public_network_access_enabled = false– this ensures your database never goes over the public Internet.ignore_changes– prevents Terraform from trying to recreate the resource when Azure automatically assigns an availability zone.
This setup gives you an enterprise-grade database — fully private and fully automated.
Step 2 — Configuring Private DNS
To make private connections work, Terraform first needs to define a DNS zone and link it to your virtual network:
resource "azurerm_private_dns_zone" "foggykitchen_pg_dns" {
name = "privatelink.postgres.database.azure.com"
resource_group_name = azurerm_resource_group.foggykitchen_rg.name
}
resource "azurerm_private_dns_zone_virtual_network_link" "foggykitchen_pg_dns_link" {
name = "pg-dns-link"
resource_group_name = azurerm_resource_group.foggykitchen_rg.name
private_dns_zone_name = azurerm_private_dns_zone.foggykitchen_pg_dns.name
virtual_network_id = azurerm_virtual_network.foggykitchen_vnet.id
}
This is where the magic happens.
Once this zone is linked, any VM inside the VNet can resolve the private PostgreSQL hostname — without exposing a single packet outside your network.
Step 3 — Creating a Database
After provisioning the Flexible Server, Terraform can immediately create databases on it:
resource "azurerm_postgresql_flexible_server_database" "foggykitchen_db" {
name = "foggydb"
server_id = azurerm_postgresql_flexible_server.foggykitchen_pg.id
collation = "en_US.utf8"
charset = "UTF8"
}
Simple, predictable, and fully automatable.
At this point, your backend VMs (for example foggykitchen_backend_vm1) can connect directly using private DNS — no firewall rules, no public endpoint, and no secrets exposed.
Step 4 — Putting It All Together
Step 5 — Lifecycle and Maintenance
When managing databases as code, the lifecycle block in Terraform becomes your best friend.
Azure sometimes moves resources across zones or adjusts metadata during maintenance.
By ignoring those non-critical changes, you keep your deployments idempotent and your CI/CD pipelines smooth.
lifecycle {
ignore_changes = [ zone ]
}
In production, this single line can save hours of troubleshooting.
FoggyKitchen Takeaway
At FoggyKitchen, we don’t just spin up managed databases — we cook them with precision.
From subnet delegation to DNS linking, every ingredient matters when you want a reliable, private, and reproducible data layer.
Azure’s abstractions make life easier, but Terraform ensures you stay in control.
And when you combine both, you get the best of both worlds: speed and transparency.
🎓 Explore More
👉 Multicloud Foundations: Azure & OCI deployed with Terraform/OpenTofu
Learn how this PostgreSQL database becomes part of a real multicloud topology — securely connected to Oracle Cloud workloads.
🔗 You might also like:

From Cloud Recipes to Real Automation
Learn how to connect Azure PostgreSQL and OCI workloads using Terraform/OpenTofu — securely, privately, and reproducibly.
Hands-on training that unifies Azure and OCI automation — compute, storage, networking, and databases — all in Terraform/OpenTofu.
🔒 Lifetime • ⏱️ Self-paced • 🧪 Real labs
