Table of Contents
Terraform Module for PostgreSql Flexible Server
Summary: This is a terraform module that I use to deploy a PostgreSql Flexible Server in Azure.
Date: 8 February 2025
Read the post to learn more about how to deploy a PostgreSql Flexible Server using terraform and:
- How to set a random password for the PostgreSql admin
- How to deploy a private endpoint for a PostgreSql Flexible Server using a Terraform module
- How to create a link between the private DNS zone and the virtual network
- How to configure diagnostic settings on the PostgreSql Flexible Server using a Terraform module
Deploying a PostgreSql Flexible Server
We will use the following terraform configuration file to deploy a PostgreSql Flexible Server. With the file, two modules are called, one for the PostgreSql Flexible Server and one for the private endpoint.
Notice the following:
- We use the terraform random module to generate a random password for the PostgreSql admin
- There is a note regarding the high availability mode, which is set to “SameZone” in this example because “ZoneRedundant” deploys are temporarily disabled in the West Europe region
- The id of the log analytics workspace is passed to the PostgreSql Flexible Server module so the diagnostic settings can be configured
- The subresource_names is set to “postgresqlServer”, which is defined by microsoft
- postgresql.tf
// random password for the PostgreSql admin resource "random_password" "postgresql_admin_password" { length = 14 special = true override_special = "!#$%&*()-_=+[]{}<>:?" } // Flexible PostgreSql Server module "postgresql_flexible_server" { source = "../modules/postgresql_flexible_server" name = var.postgresql_flexible_server_name resource_group_name = var.resource_group_name location = var.location tags = var.tags postgresql_flexible_server_version = 16 administrator_login = "psqladmin" administrator_password = random_password.postgresql_admin_password.result sku_name = "GP_Standard_D2s_v3" backup_retention_days = 8 geo_redundant_backup_enabled = true zone = 3 high_availability_mode = "SameZone" # "ZoneRedundant" - Zoneredundant deploys are temporarily disabled in region West Europe, see readme.md in module standby_availability_zone = 3 storage_mb = 32768 storage_tier = "P6" public_network_access_enabled = false law_id = module.law.id } // Private DNS Zone Virtual Network Link module "psql_private_dns_zone_vnet_link" { source = "../../modules/private_dns_zone_virtual_network_link" name = "dpl-${lower(var.vnet_name)}" private_dns_zone_name = "privatelink.postgres.database.azure.com" virtual_network_id = data.azurerm_virtual_network.virtual_network.id resource_group_name = var.private_dns_zone_resource_group_name providers = { azurerm = azurerm.hub } } // Flexible PostgreSQL Server Private Endpoint module "postgresql_flexible_server_private_endpoint" { source = "../modules/private_endpoint_with_named_nic" name = "pep-${module.postgresql_flexible_server[0].name}" location = var.location resource_group_name = var.private_endpoint_resource_group_name subnet_id = data.azurerm_subnet.private_endpoint_subnet.id tags = var.tags private_connection_resource_id = module.postgresql_flexible_server[0].id is_manual_connection = false subresource_name = "postgresqlServer" private_dns_zone_group_name = "PostgresqlPrivateDnsZoneGroup" private_dns_zone_group_ids = [module.psql_private_dns_zone_vnet_link.private_dns_zone_id] ip_configurations = var.postgresql_flexible_server_private_endpoint_ip_configurations }
Terraform Module for PostgreSql Flexible Server
The module for the PostgreSql Flexible Server is defined over four files:
- main.tf: Contains the resources for the PostgreSql Flexible Server and starts another module for the diagnostic settings
- outputs.tf: Contains the output of the PostgreSql Flexible Server
- variables.tf: Contains the input variables for the PostgreSql Flexible Server
- readme.md: Contains the documentation for the module
main.tf
- main.tf
resource "azurerm_postgresql_flexible_server" "postgresql_flexible_server" { name = var.name resource_group_name = var.resource_group_name location = var.location tags = var.tags version = var.postgresql_flexible_server_version administrator_login = var.administrator_login administrator_password = var.administrator_password sku_name = var.sku_name backup_retention_days = var.backup_retention_days geo_redundant_backup_enabled = var.geo_redundant_backup_enabled storage_mb = var.storage_mb storage_tier = var.storage_tier public_network_access_enabled = var.public_network_access_enabled zone = var.zone high_availability { mode = var.high_availability_mode standby_availability_zone = var.standby_availability_zone } } module "diagnostic_settings" { source = "../modules/diagnostic_settings_all" log_analytics_workspace_id = var.law_id targets_resource_id = [ azurerm_postgresql_flexible_server.postgresql_flexible_server.id ] }
outputs.tf
- outputs.tf
output "name" { description = "Specifies the name of the PostgreSQL Flexible server." value = azurerm_postgresql_flexible_server.postgresql_flexible_server.name } output "id" { description = "Specifies the resource id of the PostgreSQL Flexible server." value = azurerm_postgresql_flexible_server.postgresql_flexible_server.id }
variables.tf
Note that most of the descriptions are copied from the terraform registry.
- variables.tf
variable "name" { description = "(Required) The name which should be used for this PostgreSQL Flexible Server. Changing this forces a new PostgreSQL Flexible Server to be created." type = string } variable "resource_group_name" { description = "(Required) The name of the Resource Group where the PostgreSQL Flexible Server should exist. Changing this forces a new PostgreSQL Flexible Server to be created." type = string } variable "location" { description = "(Required) The Azure Region where the PostgreSQL Flexible Server should exist. Changing this forces a new PostgreSQL Flexible Server to be created." type = string } variable "postgresql_flexible_server_version" { description = "(Optional) The version of PostgreSQL Flexible Server to use. Possible values are 11,12, 13, 14, 15 and 16. Required when create_mode is Default." type = string } variable "administrator_login" { description = "(Optional) The Administrator login for the PostgreSQL Flexible Server. Required when create_mode is Default and authentication.password_auth_enabled is true." type = string } variable "administrator_password" { description = "(Optional) The Password associated with the administrator_login for the PostgreSQL Flexible Server. Required when create_mode is Default and authentication.password_auth_enabled is true." type = string sensitive = true } variable "sku_name" { description = "(Optional) The SKU Name for the PostgreSQL Flexible Server. The name of the SKU, follows the tier + name pattern (e.g. B_Standard_B1ms, GP_Standard_D2s_v3, MO_Standard_E4s_v3)." type = string } variable "backup_retention_days" { description = "(Optional) The backup retention days for the PostgreSQL Flexible Server. Possible values are between 7 and 35 days." type = number } variable "geo_redundant_backup_enabled" { description = "(Optional) Is Geo-Redundant backup enabled on the PostgreSQL Flexible Server. Defaults to false. Changing this forces a new PostgreSQL Flexible Server to be created." type = bool } variable "zone" { description = "(Optional) Specifies the Availability Zone in which the PostgreSQL Flexible Server should be located." type = string } variable "high_availability_mode" { description = "(Required) The high availability mode for the PostgreSQL Flexible Server. Possible value are SameZone or ZoneRedundant." type = string } variable "standby_availability_zone" { description = "(Optional) Specifies the Availability Zone in which the standby Flexible Server should be located." type = string } variable "storage_mb" { description = "(Optional) The max storage allowed for the PostgreSQL Flexible Server. Possible values are 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4193280, 4194304, 8388608, 16777216 and 33553408." type = number } variable "storage_tier" { description = "(Optional) The name of storage performance tier for IOPS of the PostgreSQL Flexible Server. Possible values are P4, P6, P10, P15,P20, P30,P40, P50,P60, P70 or P80. Default value is dependant on the storage_mb value. Please see the storage_tier defaults based on storage_mb table below." type = string } variable "public_network_access_enabled" { description = "(Optional) Specifies whether this PostgreSQL Flexible Server is publicly accessible. Defaults to true." type = bool } variable "tags" { description = "(Optional) A mapping of tags to assign to the resource." type = map(any) default = {} } variable "law_id" { description = "(Required) Specifies the resource id of the log analytics workspace." type = string }
readme.md
Notice how I use the readme to explain design choices, temporary issues, and links to documentation.
- readme.md
# PostgreSql Flexible Server This module is the replacement for the PostgreSql Standalone server which retires in March 2025. ## Useful links - [Terraform registry](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/postgresql_flexible_server) - [Azure Database for PostgreSQL - Flexible Server documentation](https://learn.microsoft.com/en-us/azure/postgresql/) ## Notes - version = "16" : requires newer version (3.110) for azurerm provider - compute: - sku_name = "GP_Standard_D2s_v3" // Standard_D2s_v3 (2 vCores, 8 GiB memory, 3200 max iops) - zone = "3" : This is the availability zone in which the server will be deployed - Note: I removed this option because of an error during the deployment. I suspect the resources in West Europe in zone 1 and 2 to be scarce. When removing the zone preference the deployment succeeded in zone 3. Backup: - backup_retention_days = 8 : backups are retained for 8 days - geo_redundant_backup_enabled = true : backup redundancy is set to Geo-Redundant - [high_availability](https://learn.microsoft.com/en-us/azure/reliability/reliability-postgresql-flexible-server#availability-zone-support) - mode = "SameZone" : high availability is set for the same zone, so if the server crashes another server is (immediately) available. If we want to protect for zone failure we need the following settings: - mode = "ZoneRedundant" - standby_availability_zone = "1" > Even though the region 'West Europe' is enabled for ZoneRedundant deployments, such deployments are temporarily [blocked](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/overview#azure-regions) ### Procedure to move to 'ZoneRedundant' once possible - Disable HA (could be done in the portal or by commenting out the 'high_availability' block in the tf module) - Configure the high_availability block as described above - [Storage size and tier](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/postgresql_flexible_server#storage_tier-defaults-based-on-storage_mb) - storage_mb = 32768 - storage_tier = "P6" // default = "P4" // P6 (240 iops) ## More information on high availability Zonal (SameZone in terraform) offers SLA uptime of 99.95%. Zone-redundancy offers [99.99%](https://learn.microsoft.com/en-us/azure/reliability/reliability-postgresql-flexible-server#sla) ## Restore a server [Restore a server (PITR/GeoRestore)](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/how-to-restore-server-portal)
Other Terraform Modules
The code above uses additional modules to deploy the private endpoint for the PostgreSql Flexible Server and to configure the diagnostic settings. For more information on these modules, see: