Deploying a highly available Amazon RDS architecture with Terraform requires a clear separation between the database cluster definition and the individual cluster instances that provide compute. An RDS cluster represents the shared storage layer and configuration that is common to all nodes, while the instances provide the writer and reader endpoints. Using Terraform to codify aws_rds_cluster together with aws_rds_cluster_instance makes it possible to build Aurora MySQL and PostgreSQL clusters that are repeatable, version controlled and auditable across development, staging and production environments.
Prerequisites and Provider Configuration
Before starting, ensure you have the following:
- AWS Account: An active AWS account with the necessary permissions.
- AWS CLI: AWS CLI should be configured with your AWS credentials.
- Terraform Installed: Terraform must be installed on your local machine. You can download it from Terraform’s official site.
The provider file tells Terraform which provider you are using. A multi-region active/active design requires multiple provider aliases, one per region, with consistent default tags for ownership and project tracking.
```hcl
provider "aws" {
region = local.region0
profile = "
default
tags = {
Owner = "primary"
Project = "AWS Multi Region rds with active/active setup"
Provisioner = "Terraform"
}
}
}
provider "aws" {
alias = "secondory"
region = local.region1
profile = "
default
tags = {
Owner = "secondory"
Project = "AWS Multi Region rds with active/active setup"
Provisioner = "Terraform"
}
}
}
```
Terraform data sources allow you to fetch information from your cloud provider and use it within your configuration. Data sources are used to discover existing VPCs, subnets, security groups and Secrets Manager secrets before referencing them in aws_rds_cluster and instance resources.
Understanding awsrdscluster Resource
aws_rds_cluster provides an RDS Cluster Resource. A Cluster Resource defines attributes that are applied to the entire cluster of RDS Cluster Instances. Use the RDS Cluster resource and RDS Cluster Instances to create and use Amazon Aurora, a MySQL-compatible database engine. For more information on Amazon Aurora, see Aurora on Amazon RDS in the Amazon RDS User Guide.
Changes to a RDS Cluster can occur when you manually change a parameter, such as port, and are reflected in the next maintenance window. Because of this, Terraform may report a difference in its planning phase because a modification has not yet taken place. You can use the apply_immediately flag to instruct the service to apply the change immediately.
Note: using apply_immediately can result in a brief downtime as the server reboots
The cluster resource is the control plane for shared storage, engine version, backup retention and cluster-wide parameters. Individual compute is added via separate aws_rds_cluster_instance resources.
Single Instance vs Cluster and Engine Support
It supports engines like MySQL, PostgreSQL, MariaDB, Oracle, and SQL Server and handles routine tasks such as backups, patching, and high availability.
Terraform integrates with RDS by using the AWS provider to define infrastructure as code. You can declare aws_db_instance or aws_rds_cluster resources in Terraform to create and manage RDS instances or clusters. Terraform plans changes, applies them consistently, and tracks the state of your RDS configurations, making infrastructure deployment repeatable and auditable. Using Terraform with RDS ensures environment consistency, improves version control of database configurations, and enables automation in CI/CD pipelines.
| Resource | Scope | Storage Model | Typical Use |
|---|---|---|---|
aws_db_instance |
Single database | Instance-local storage | MySQL, PostgreSQL, MariaDB, Oracle, SQL Server standalone |
aws_rds_cluster |
Cluster with shared storage | Aurora shared storage layer | Aurora MySQL / Aurora PostgreSQL with writer and reader nodes |
aws_db_instance provisions a single RDS database, while aws_rds_cluster creates an Aurora cluster with a shared storage layer and separate aws_rds_cluster_instance resources for each node.
A basic single instance example from the reference configuration is:
hcl
resource "aws_db_instance" "default" {
allocated_storage = 10
engine = "mysql"
instance_class = "db.t3.micro"
username = "foo"
password = "foobarbaz"
skip_final_snapshot = true
}
Attributes:
allocated_storage: Memory allocated in GB for this database instance.engine: Choice of database engine. We have selected MySQL as the desired database engine. We can also select Postgres, MariaDB, Oracle, etc
Secure Credential Management and Read Replicas
How do I store RDS passwords securely in Terraform?
Store passwords in AWS Secrets Manager or SSM Parameter Store and reference them with a data source, or use the manage_master_user_password argument to let RDS handle rotation automatically. Avoid hardcoding credentials in .tf files or state.
Can Terraform manage RDS read replicas?
Yes. Set the replicate_source_db argument on a second aws_db_instance resource, pointing it to the primary instance’s identifier.
OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.
Spacelift orchestrates your Terraform workflows end to end, including state management, policy as code, drift detection, resource visualization, context sharing, programmatic configuration, and support for complex, multi-step workflows.
Module Based Cluster Provisioning
A Terraform module for managing a simple Aurora Postgres cluster gets a list of inputs, and creates an Aurora Postgres Cluster, with a configurable number of instances. In addition, it creates a secret on AWS Secrets Manager, to store credentials to access the recently created cluster. This output secret has the root user, password, endpoint and reader_endpoint, that represents the read-only endpoint for the Aurora cluster, automatically load-balanced across replicas. You can see more information on aws_rds_cluster documentation.
There is a naming convention for the created resources, and the caller is allowed to provide some prefixes and suffixes, that are used to build the names.
Cloud Posse provides a module to provision an RDS Aurora cluster for MySQL or Postgres. Supports Amazon Aurora Serverless.
hcl
module "rds_cluster_aurora_postgres" {
source = "cloudposse/rds-cluster/aws"
name = "postgres"
engine = "aurora-postgresql"
cluster_family = "aurora-postgresql9.6"
cluster_size = 2
namespace = "eg"
stage = "dev"
admin_user = "admin1"
admin_password = "Test123456789"
db_name = "dbname"
db_port = 5432
instance_type = "db.r4.large"
vpc_id = "vpc-xxxxxxxx"
security_groups = ["sg-xxxxxxxx"]
subnets = ["subnet-xxxxxxxx", "subnet-xxxxxxxx"]
zone_id = "Zxxxxxxxx"
}
The example shows 1 writer, 1 reader with cluster_size = 2. Alternatives are:
- 1 writer, 3 reader with
cluster_size = 4 - 1 writer, 5 reader with
cluster_size = 6
Serverless variant:
hcl
module "rds_cluster_aurora_mysql_serverless" {
source = "cloudposse/rds-cluster/aws"
namespace = "eg"
stage = "dev"
name = "db"
engine = "aurora"
engine_mode = "serverless"
cluster_family = "aurora5.6"
cluster_size = 0
admin_user = "admin1"
admin_password = "Test123456789"
db_name = "dbname"
db_port = 3306
instance_type = "db.t2.small"
vpc_id = "vpc-xxxxxxxx"
security_groups = ["sg-xxxxxxxx"]
subnets = ["subnet-xxxxxxxx", "subnet-xxxxxxxx"]
zone_id = "Zxxxxxxxx"
enable_http_endpoint = true
scaling_configuration = [
{
auto_pause = true
max_capacity = 256
min_capacity =
}
]
}
Tip: For a complete example, see examples/complete.
Multi-Region RDS Cluster with Automatic Failover
In this article, we will go through how to deploy a multi-region AWS RDS cluster with an automatic failover setup using Terraform. By leveraging AWS RDS and Terraform, we can set up highly available, fault-tolerant database architectures across multiple regions. This ensures that your applications remain online even in the event of regional outages, providing resilience and scalability for critical applications.
The multi-region pattern relies on two provider configurations, one per region, with distinct default tags for Owner and secondary designation. The primary region hosts the writer cluster and the secondary region hosts a read replica cluster or a standby cluster that can be promoted. Terraform data sources are used to fetch regional endpoints, hosted zone IDs and Secrets Manager ARNs so the configuration remains decoupled from hardcoded values.
Timeouts, Import and Routing Attributes
aws_rds_cluster provides the following Timeouts configuration options:
| Timeout | Default | Purpose |
|---|---|---|
| create | 120 minutes | Used for Cluster creation |
| update | 120 minutes | Used for Cluster modifications |
| delete | 120 minutes | Used for destroying cluster. This includes any cleanup task during the destroying process |
hosted_zone_id - The Route53 Hosted Zone ID of the endpoint
RDS Clusters can be imported using the cluster_identifier, e.g.
bash
$ terraform import aws_rds_cluster.aurora_cluster aurora-prod-cluster
The import command maps an existing Aurora cluster to the Terraform state so subsequent changes can be managed declaratively.
Conclusion
Terraform management of aws_rds_cluster provides a consistent way to codify Aurora MySQL and PostgreSQL clusters with shared storage, automated backups, patching and high availability across engines including MySQL, PostgreSQL, MariaDB, Oracle and SQL Server. The aws_rds_cluster resource defines cluster-wide attributes while aws_rds_cluster_instance resources define per-node compute, enabling writer-reader topologies with a load balanced reader endpoint. For multi-region resilience, multiple provider aliases with region-specific tags enable an active/active or primary-secondary deployment that survives regional outages.
Secure credential handling is achieved by avoiding hard coded passwords in .tf files and instead referencing Secrets Manager or SSM Parameter Store, or by enabling manage_master_user_password for automatic rotation. Modules such as the Aurora Postgres module with Secrets Manager integration and the Cloud Posse rds-cluster/aws module with support for Aurora Serverless reduce boilerplate and enforce naming conventions with prefixes and suffixes supplied by the caller.
Operational considerations include the maintenance window behavior for parameter changes like port, the planning drift that can occur when changes are not yet applied, and the use of apply_immediately with awareness of brief downtime due to reboot. Timeouts of 120 minutes for create, update and delete operations reflect the long-running nature of cluster provisioning and teardown. Importing existing clusters by cluster_identifier allows adoption of Terraform for brownfield environments.
Together, provider aliasing, data sources, module reuse and explicit timeout and import configuration enable production-grade, repeatable and auditable RDS cluster deployments that support CI/CD pipelines and multi-region failover requirements.
Sources
- dev.to/aws-builders/terraform-deploying-multi-region-aws-rds-cluster-with-failover-setup-using-terraform-4ahg
- www.koding.com/docs/terraform/providers/aws/r/rds_cluster.html/
- spacelift.io/blog/terraform-aws-rds
- github.com/madelabs/terraform-aws-rds-cluster
- docs.w3cub.com/terraform/providers/aws/r/rds_cluster.html
- github.com/cloudposse/terraform-aws-rds-cluster