Aurora clusters incur AWS charges while running. The instance classes used in this example (db.r8g.2xlarge, db.r8g.large) are not free-tier eligible. Destroy the cluster when you are done to avoid unexpected costs.
Prerequisites
Before you begin, ensure you have the following:
-
Terraform
>= 1.11.1 installed. Verify with:
-
AWS credentials configured in your environment. The IAM principal needs permissions to create VPC resources, RDS clusters, IAM roles, KMS keys, CloudWatch log groups, and security groups.
aws configure
# or set environment variables
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_REGION="eu-west-1"
-
AWS provider configured in your Terraform root module:
terraform {
required_version = ">= 1.11.1"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 6.28"
}
}
}
provider "aws" {
region = "eu-west-1"
}
Write the configuration
Create a directory for your configuration and add the following files. This example is taken directly from the module’s PostgreSQL example and provisions a three-instance Aurora PostgreSQL 17.5 cluster with a custom VPC, parameter groups, CloudWatch log exports, custom endpoints, and an activity stream.The example sets skip_final_snapshot = true, which means no final snapshot is created when you destroy the cluster. This is appropriate for development and testing environments. Remove this argument or set it to false for production clusters.
provider "aws" {
region = local.region
}
data "aws_availability_zones" "available" {
# Exclude local zones
filter {
name = "opt-in-status"
values = ["opt-in-not-required"]
}
}
locals {
name = "ex-postgresql"
region = "eu-west-1"
vpc_cidr = "10.0.0.0/16"
azs = slice(data.aws_availability_zones.available.names, 0, 3)
tags = {
Example = local.name
GithubRepo = "terraform-aws-rds-aurora"
GithubOrg = "terraform-aws-modules"
}
}
################################################################################
# RDS Aurora Module
################################################################################
module "aurora" {
source = "terraform-aws-modules/rds-aurora/aws"
name = local.name
engine = "aurora-postgresql"
engine_version = "17.5"
master_username = "root"
storage_type = "aurora-iopt1"
cluster_monitoring_interval = 30
instances = {
1 = {
instance_class = "db.r8g.2xlarge"
publicly_accessible = true
db_parameter_group_name = "default.aurora-postgresql14"
}
2 = {
identifier = "static-member-1"
instance_class = "db.r8g.2xlarge"
}
3 = {
identifier = "excluded-member-1"
instance_class = "db.r8g.large"
promotion_tier = 15
}
}
endpoints = {
static = {
identifier = "static-custom-endpt"
type = "ANY"
static_members = ["static-member-1"]
tags = { Endpoint = "static-members" }
}
excluded = {
identifier = "excluded-custom-endpt"
type = "READER"
excluded_members = ["excluded-member-1"]
tags = { Endpoint = "excluded-members" }
}
}
vpc_id = module.vpc.vpc_id
db_subnet_group_name = module.vpc.database_subnet_group_name
security_group_ingress_rules = {
private-az1 = {
cidr_ipv4 = element(module.vpc.private_subnets_cidr_blocks, 0)
}
private-az2 = {
cidr_ipv4 = element(module.vpc.private_subnets_cidr_blocks, 1)
}
private-az3 = {
cidr_ipv4 = element(module.vpc.private_subnets_cidr_blocks, 2)
}
}
apply_immediately = true
skip_final_snapshot = true
engine_lifecycle_support = "open-source-rds-extended-support-disabled"
cluster_parameter_group = {
name = local.name
family = "aurora-postgresql17"
description = "${local.name} example cluster parameter group"
parameters = [
{
name = "log_min_duration_statement"
value = 4000
apply_method = "immediate"
},
{
name = "rds.force_ssl"
value = 1
apply_method = "pending-reboot"
}
]
}
db_parameter_group = {
name = local.name
family = "aurora-postgresql17"
description = "${local.name} example DB parameter group"
parameters = [
{
name = "log_min_duration_statement"
value = 4000
apply_method = "immediate"
}
]
}
enabled_cloudwatch_logs_exports = ["postgresql"]
create_cloudwatch_log_group = true
cluster_activity_stream = {
kms_key_id = module.kms.key_id
mode = "async"
}
tags = local.tags
}
################################################################################
# Supporting Resources
################################################################################
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 6.0"
name = local.name
cidr = local.vpc_cidr
azs = local.azs
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)]
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 3)]
database_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 6)]
tags = local.tags
}
module "kms" {
source = "terraform-aws-modules/kms/aws"
version = "~> 4.0"
deletion_window_in_days = 7
description = "KMS key for ${local.name} cluster activity stream"
enable_key_rotation = true
is_enabled = true
key_usage = "ENCRYPT_DECRYPT"
aliases = ["rds/${local.name}"]
tags = local.tags
}
Initialize and apply
Initialize the working directory to download the module and provider, then review and apply the plan.Review the resources Terraform will create before applying:Apply the configuration. Type yes when prompted to confirm:Provisioning an Aurora cluster typically takes 10–15 minutes. Terraform will print output values when the apply completes. Access the cluster endpoint
After a successful apply, retrieve the connection details from the Terraform outputs:terraform output cluster_endpoint
terraform output cluster_reader_endpoint
terraform output cluster_port
terraform output cluster_master_user_secret
The cluster_endpoint output is the writer endpoint you use for read/write connections. The cluster_reader_endpoint is a load-balanced read-only endpoint across all reader instances.The master user password is managed by AWS Secrets Manager. Retrieve it with the AWS CLI using the secret ARN from the cluster_master_user_secret output:aws secretsmanager get-secret-value \
--secret-id "$(terraform output -raw cluster_master_user_secret | jq -r '.secret_arn')" \
--query SecretString \
--output text
Connect to the cluster using the writer endpoint:psql -h "$(terraform output -raw cluster_endpoint)" \
-U root \
-p "$(terraform output -raw cluster_port)"
Clean up
To remove all resources created by this example and stop incurring charges:Type yes when prompted. Because skip_final_snapshot = true is set in this example, no final snapshot will be created before the cluster is deleted.