Showing posts with label Terraform. Show all posts
Showing posts with label Terraform. Show all posts

January 9, 2026

PostgreSQL Major Version Upgrades on Azure: A Terraform-based Approach

Introduction

PostgreSQL 11 has reached its end of life, and Azure recommends upgrading to PostgreSQL 13 or later for enhanced security, improved performance, and long-term support. Unlike minor upgrades, Azure Database for PostgreSQL (Flexible Server) does not support in-place major version upgrades. This makes the upgrade process slightly non-trivial—especially when the server is provisioned using Terraform, and some environments use VNet integration.

In this blog, we’ll walk through:
  • How Azure PostgreSQL upgrades work
  • Why does Terraform recreate the server
  • Multiple migration strategies
  • The exact steps I followed to upgrade PostgreSQL 11 → 13 safely


Existing Setup

My environment had the following characteristics:

  • Azure Database for PostgreSQL – Flexible Server
  • PostgreSQL version: 11
  • SKU: Burstable B1ms (1 vCore, 2 GiB RAM)
  • Storage: 32 GiB
  • Region: Central US
  • Provisioned using Terraform
  • Mixed environments: Some with public access, some with VNet integration
  • Firewall rules restricted to specific IPs

Terraform snippet (simplified):



Important Reality: No In-Place Major Version Upgrade

This is the most critical thing to understand: Azure PostgreSQL Flexible Server does NOT support in-place major version upgrades.

That means:
  • You cannot upgrade PostgreSQL 11 → 13 on the same server
  • Changing version = "13" in Terraform:
  • Deletes the existing PostgreSQL 11 server
  • Creates a brand-new PostgreSQL 13 server
  • All data is lost unless you migrate or restore it manually

 

Terraform makes this very clear: forces replacement. This is not really an upgrade — it’s a rebuild and a migration.

Why This Upgrade Looks Simple — and Why It Isn’t

At first glance, the upgrade appears trivial: version = "13" 

But behind this single line:
  • Azure treats PostgreSQL major versions as immutable
  • Terraform maps this to a ForceNew operation
  • Automated backups are tied to the old server lifecycle
  • Configuration and data do not carry over


What Actually Happens (Timeline)

Understanding the timeline helps avoid surprises:

T-0: PostgreSQL 11 running

  • Applications connected
  • Data live
  • Automated backups available


T-1: Terraform version updated

  • version = "11" → version = "13"
  • Plan shows forces replacement


T-2: Terraform apply

  • PostgreSQL 11 server is deleted
  • Databases and backups disappear


T-3: PostgreSQL 13 server created

  • Empty server
  • Default parameters
  • No firewall rules
  • No databases


T-4: Manual restore

  • Data restored
  • Configuration reapplied
  • Applications reconnect


Available Upgrade Approaches

1. Azure Database Migration Service (DMS)
2. Backup & Restore (pg_dump / pgAdmin)
3. Temporary Public Access

Here we focus on Option 3, which was simple, cost-effective, and acceptable for my downtime window.

Step 1: Take a Backup

I used pgAdmin 4 with a custom format backup.

Why Custom format?
  • Includes schema + data
  • Best compatibility across versions
  • Works cleanly with pg_restore
pg_dump `
  -h myserver.postgres.database.azure.com `
  -U pgsqladmin@myserver `
  -d master_data_service `
  -Fc `
  --sslmode=require `
  -f master_data_service_v11.dump

Step 2: Upgrade PostgreSQL Version via Terraform

In Terraform, change the code: version = "13"
Important: This destroys the PostgreSQL 11 server and creates a new PostgreSQL 13 server with the same name.
Run:
terraform plan
terraform apply

This immediately destroys the PostgreSQL 11 server and creates a new PostgreSQL 13 server with the same name.

Step 3: Restore the Database to PostgreSQL 13

pg_restore `
  -h myserver.postgres.database.azure.com `
  -U pgsqladmin@myserver `
  -d postgres `
  --create `
  -Fc `
  --sslmode=require `
  master_data_service_v11.dump

This:
  • Recreated the database
  • Restored schema and data
  • Worked cleanly from v11 → v13


Step 4: Server Parameters & Configuration
Azure applies default server parameters when a new PostgreSQL server is created.

Key learning:

  • Server parameters are NOT automatically migrated
  • If you changed parameters manually in the portal, you must reapply them


Step 5: VNet-Integrated Environments

For servers with VNet integration:
  • No public endpoint exists
  • Local pgAdmin / pg_dump won’t connect

Available options:
  • Use Azure DMS inside the VNet
  • Use a VM or jumpbox
  • Temporarily enable public access

We temporarily enabled public access with strict /32 firewall rules and disabled it immediately after migration.

Step 6: Validate & Cutover

After restoring:
  • Verified tables, row counts, and extensions
  • Tested application connectivity
  • Updated connection strings where required
  • Disabled public access again for private environments

Cost Considerations
  • PostgreSQL B1ms server: ~$25/month
  • Temporary overlap or migration time: a few dollars
  • Azure DMS (Standard): Often free for migration scenarios
  • Overall upgrade cost: minimal


Key Takeaways

  • Azure PostgreSQL major upgrades are not in-place
  • Terraform recreates the server when version changes
  • Always backup before upgrading
  • Server parameters must be reapplied
  • For VNet setups, plan connectivity carefully
  • PostgreSQL supports direct jump from 11 → 13


Final Thoughts

Upgrading PostgreSQL on Azure requires careful planning, but with the right approach, it can be a predictable and safe process.

If you’re using Terraform:

  • Treat major version upgrades as rebuild + restore
  • Automate as much as possible
  • Test in lower environments first

April 3, 2025

Managing Multiple Azure Environments with Terraform

Introduction

Managing cloud infrastructure across multiple environments can be complex. Terraform simplifies this process using modules and workspaces, Allows us more efficient and scalable infrastructure management in any cloud. This guide explores leveraging Terraform modules in a multi-workspace setup for Microsoft Azure. 

Benefits of Terraform Modules and Workspaces

Terraform Modules: Enhancing Reusability

Modules allow infrastructure components to be defined once and reused across different environments. This reduces redundancy and enhances maintainability.


Terraform Workspaces: Isolating Environments

Workspaces create separate states for different environments, ensuring isolation and preventing conflicts between deployments. Utilizing Terraform variables further refines environment-specific configurations.


Structuring Terraform for Multi-Environment Deployment

A well-structured Terraform directory simplifies management across environments. Below is a recommended directory structure:


Directory Layout

$ tree complete-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
├── ...
├── modules/
│   ├── nestedA/
│   │   ├── README.md
│   │   ├── variables.tf
│   │   ├── main.tf
│   │   ├── outputs.tf
│   ├── nestedB/
│   ├── .../
├── examples/
│   ├── exampleA/
│   │   ├── main.tf
│   ├── exampleB/
│   ├── .../

Creating a Reusable Terraform Module

Defining a Virtual Network Module:

 - modules/network/main.tf
resource "azurerm_virtual_network" "network" {
  name                = var.network_name
  location            = var.location
  resource_group_name = var.resource_group_name
  address_space       = var.address_space
}

 - modules/network/variables.tf

variable "network_name" {
  type = string
}

variable "location" {
  type = string
}

variable "resource_group_name" {
  type = string
}

variable "address_space" {
  type = list(string)
}

 - modules/network/outputs.tf

output "network_id" {
  value = azurerm_virtual_network.network.id
}

Utilizing the Module in the Main Configuration

- main.tf

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "4.16.0"
    }
  }

  backend "azurerm" {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "terraformstate"
    container_name       = "tfstate"
    key                  = "terraform.tfstate"
  }
}

provider "azurerm" {
  features {}
}

module "network" {
  source              = "./modules/network"
  network_name        = "my-network-${terraform.workspace}"
  location            = "East US"
  resource_group_name = "my-rg"
  address_space       = ["10.0.0.0/16"]
}

Managing Workspaces for Different Environments

Initializing and Creating Workspaces

Run the following commands to initialize Terraform and create new workspaces:

terraform init
terraform workspace new development
terraform workspace new staging
terraform workspace new production

Switch between workspaces:

terraform workspace select development

Applying Configuration to a Specific Workspace

terraform apply -var-file=environments/development.tfvars

Terraform plan output:

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.network.azurerm_virtual_network.network will be created
  + resource "azurerm_virtual_network" "network" {
      + address_space       = ["10.0.0.0/16"]
      + id                  = (known after apply)
      + location            = "East US"
      + name                = "my-network-default"
      + resource_group_name = "my-rg"
    }

Plan: 1 to add, 0 to change, 0 to destroy.


Terraform apply output:

module.network.azurerm_virtual_network.network: Creating...
module.network.azurerm_virtual_network.network: Creation complete after 30s ...

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

network_id = "/.../my-rg/.../Microsoft.Network/virtualNetworks/my-network-default"

Advantages of This Approach

  • Code Efficiency: Reusable modules minimize code duplication.
  • Environment Segregation: Workspaces ensure different state for different environment.
  • Scalability: With this approach we can easily add multiple environments as needed.

Reference links:


Conclusion

Using Terraform modules and workspaces in Azure streamlines environment management, improves reusability, and enhances scalability. This structured approach keeps infrastructure organized and adaptable to change.

Happy Terraforming!