Database migration is one of the most challenging aspects of infrastructure management. Whether you are migrating from on-premises to AWS, between database engines, upgrading major versions, or restructuring your database architecture, Terraform can automate and standardize the process. This guide covers using Terraform for database migrations with AWS Database Migration Service, Blue-Green deployments, and schema migration strategies. Terraform excels at the infrastructure provisioning aspects of migration while complementary tools handle the data migration and schema changes.
Module Overview
The Terraform module which creates AWS DMS Database Migration Service resources provides a consolidated interface for provisioning the full DMS stack. See examples directory for working examples to reference.
The module is published as terraform-aws-modules/dms/aws with version constraint ~> 2.0. The module accepts subnet group configuration, replication instance configuration, and endpoint definitions in a single module block.
Module Resources
The module encapsulates the following resource types:
| Type | Resource |
|---|---|
| resource | awsdmscertificate.this |
| resource | awsdmsendpoint.this |
| resource | awsdmsevent_subscription.this |
| resource | awsdmsreplication_config.this |
| resource | awsdmsreplication_instance.this |
| resource | awsdmsreplicationsubnetgroup.this |
| resource | awsdmsreplication_task.this |
| resource | awsdmss3_endpoint.this |
| resource | awsiampolicy.access |
| resource | awsiamrole.access |
| resource | awsiamrole.dmsaccessfor_endpoint |
| resource | awsiamrole.dmscloudwatchlogs_role |
| resource | awsiamrole.dmsvpcrole |
| resource | awsiamrolepolicyattachment.access |
| resource | awsiamrolepolicyattachment.access_additional |
| resource | timesleep.waitfordependencyresources |
Module Data Sources
| Data Source | Description |
|---|---|
| awscalleridentity.current | |
| awsiampolicy_document.access | |
| awsiampolicydocument.accessassume | |
| awsiampolicydocument.dmsassume_role | |
| awsiampolicydocument.dmsassumeroleredshift | |
| aws_partition.current | |
| aws_region.current |
Module Inputs
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| accessiamrole_description | Description of the role | string | null | no |
| accessiamrole_name | Name to use on IAM role created | string | null | no |
| accessiamrole_path | IAM role | string | null | no |
The complete example configuration creates:
- AWS IAM roles necessary for AWS DMS
- AWS DMS subnet group
- AWS DMS replication instance
- Two AWS DMS replication endpoints - one source and one target to migrate data from an Aurora PostgreSQL cluster to Aurora MySQL cluster
- AWS DMS replication task
- Two AWS DMS event subscriptions - one for the replication instance, and one for the replication task
- Necessary supporting resources to demonstrate the capabilities of the module
Replication Instance Configuration
A typical module invocation configures the replication instance and endpoints together:
```hcl
module "databasemigrationservice" {
source = "terraform-aws-modules/dms/aws"
version = "~> 2.0"
replsubnetgroupname = "example"
replsubnetgroupdescription = "DMS Subnet group"
replsubnetgroupsubnetids = ["subnet-1fe3d837", "subnet-129d66ab", "subnet-1211eef5"]
replinstanceallocatedstorage = 64
replinstanceautominorversionupgrade = true
replinstanceallowmajorversionupgrade = true
replinstanceapplyimmediately = true
replinstanceengineversion = "3.5.2"
replinstancemultiaz = true
replinstancepreferredmaintenancewindow = "sun:10:30-sun:14:30"
replinstancepubliclyaccessible = false
replinstanceclass = "dms.t3.large"
replinstanceid = "example"
replinstancevpcsecuritygroupids = ["sg-12345678"]
endpoints = {
source = {
databasename = "example"
endpointid = "example-source"
endpointtype = "source"
enginename = "aurora-postgresql"
extraconnectionattributes = "heartbeatFrequency=1;"
username = "postgresqlUser"
password = "youShouldPickABetterPassword123!"
port = 5432
servername = "dms-ex-src.cluster-abcdefghijkl.us-east-1.rds.amazonaws.com"
sslmode = "none"
tags = { EndpointType = "source" }
}
destination = {
databasename = "example"
endpointid = "example-destination"
endpointtype = "target"
enginename = "aurora"
username = "mysqlUser"
password =
}
}
}
```
The replication instance parameters control capacity and maintenance behavior. repl_instance_allocated_storage is set to 64. repl_instance_auto_minor_version_upgrade is true. repl_instance_allow_major_version_upgrade is true. repl_instance_apply_immediately is true. repl_instance_engine_version is 3.5.2. repl_instance_multi_az is true. repl_instance_preferred_maintenance_window is sun:10:30-sun:14:30. repl_instance_publicly_accessible is false. repl_instance_class is dms.t3.large.
Native Terraform Resources for DMS
Terraform provides native resources for DMS infrastructure.
The AWS provider exposes 8 Terraform resources and 5 data sources available for DMS.
Resource list:
aws_dms_certificate- ResourceManages an Dms Certificate resource.aws_dms_endpoint- ResourceManages an Dms Endpoint resource.aws_dms_event_subscription- ResourceManages an Dms Event Subscription resource.aws_dms_replication_config- ResourceManages an Dms Replication Config resource.aws_dms_replication_instance- ResourceManages an Dms Replication Instance resource.aws_dms_replication_subnet_group- ResourceManages an Dms Replication Subnet Group resource.aws_dms_replication_task- ResourceManages an Dms Replication Task resource.aws_dms_s3_endpoint- ResourceManages an Dms S3 Endpoint resource.
Migration Types and Prerequisites
Database migrations generally fall into several categories.
- Engine migration involves moving from one database engine to another for example, Oracle to PostgreSQL.
- Version upgrade means moving to a newer version of the same engine.
- Cloud migration is moving from on-premises or another cloud to AWS.
- Schema migration refers to making structural changes to your database tables and objects.
- Architecture migration involves moving from single-instance to multi-AZ, or from RDS to Aurora.
Prerequisites for Terraform DMS work:
- Terraform 1.0 or later
- An AWS account
- Source and target database configurations
- Understanding of your data migration requirements
AWS Database Migration Service handles the actual data transfer. Terraform provisions the DMS infrastructure.
Network and Security Provisioning
A complete DMS networking setup provisions VPC, subnets, subnet group, and security group.
```hcl
provider "aws" {
region = "us-east-1"
}
resource "awsvpc" "migration" {
cidrblock = "10.0.0.0/16"
enablednssupport = true
enablednshostnames = true
tags = {
Name = "migration-vpc"
}
}
data "awsavailabilityzones" "available" {
state = "available"
}
resource "awssubnet" "private" {
count = 3
vpcid = awsvpc.migration.id
cidrblock = cidrsubnet(awsvpc.migration.cidrblock, 8, count.index + 10)
availabilityzone = data.awsavailability_zones.available.names[count.index]
tags = {
Name = "migration-private-${count.index + 1}"
}
}
resource "awsdmsreplicationsubnetgroup" "main" {
replicationsubnetgroupdescription = "DMS replication subnet group"
replicationsubnetgroupid = "dms-replication-subnet-group"
subnetids = awssubnet.private[*].id
tags = {
Name = "dms-subnet-group"
}
}
resource "awssecuritygroup" "dms" {
nameprefix = "dms-"
vpcid = aws_vpc.migration.id
description = "Security group for DMS replication instance"
egress {
fromport = 3306
toport = 3306
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "MySQL source"
}
egress {
fromport = 5432
toport = 5432
protocol = "tcp"
cidrblocks = [awsvpc.migration.cidr_block]
description = "PostgreSQL target"
}
egress {
fromport = 443
toport = 443
protocol =
}
}
```
Monitoring with CloudWatch Alarms
Operational visibility for DMS replication is provided via CloudWatch metric alarms.
```hcl
resource "awscloudwatchmetricalarm" "dmscdclatency" {
alarmname = "dms-cdc-latency"
comparisonoperator = "GreaterThanThreshold"
evaluationperiods = 3
metricname = "CDCLatencySource"
namespace = "AWS/DMS"
period = 300
statistic = "Average"
threshold = 60
alarmdescription = "DMS CDC latency from source is high"
dimensions = {
ReplicationInstanceIdentifier = awsdmsreplicationinstance.main.replicationinstanceid
ReplicationTaskIdentifier = awsdmsreplicationtask.migration.replicationtaskid
}
}
resource "awscloudwatchmetricalarm" "dmsfullload" {
alarmname = "dms-full-load-throughput"
comparisonoperator = "LessThanThreshold"
evaluationperiods = 3
metricname = "FullLoadThroughputRowsSource"
namespace = "AWS/DMS"
period = 300
statistic = "Average"
threshold = 100
alarmdescription = "DMS full load throughput is low"
dimensions = {
ReplicationInstanceIdentifier = awsdmsreplicationinstance.main.replicationinstanceid
ReplicationTaskIdentifier = awsdmsreplicationtask.migration.replicationtaskid
}
}
```
Schema Migration with Terraform Provisioners
While Terraform should not typically manage database schemas, you can use provisioners for initial setup.
hcl
resource "null_resource" "schema_migration" {
triggers = {
db_instance_id = aws_db_instance.target.id
migration_hash = filemd5("${path.module}/migrations/latest.sql")
}
provisioner "local-exec" {
command = <<-EOT
PGPASSWORD=${var.target_db_password} psql \
-h ${aws_db_instance.target.address} \
-U admin \
-d appdb \
-f ${path.module}/migrations/latest.sql
EOT
environment = {
PGPASSWORD =
}
}
}
Example Complete Requirements
To run this example you need to execute:
$ terraform init
$ terraform plan
$ terraform apply
Note that this example may create resources which will incur monetary charges on your AWS bill. Run terraform destroy when you no longer need these resources.
Version constraints for the example:
| Name | Version |
|---|---|
| terraform | >= 1.0 |
| aws | >= 5.96 |
| Name | Version |
|---|---|
| aws | >= 5.96 |
| Name | Source | Version |
|---|---|---|
| dmsaurorapostgresqlauroramysql | ../.. | n/a |
| dms_default | ../.. | n/a |
| dms_disabled | ../. |
Conclusion
The Terraform approach to AWS DMS centers on separating infrastructure provisioning from data movement. The terraform-aws-modules/terraform-aws-dms module encapsulates IAM roles, subnet groups, replication instances, endpoints, replication tasks, and event subscriptions into a reusable construct with explicit parameters for instance class, storage, maintenance window, multi-AZ, and security group attachment. Native provider resources such as awsdmsreplicationinstance, awsdmsendpoint, awsdmsreplicationsubnetgroup, and awsdmsreplicationtask provide granular control when custom networking or monitoring is required.
Depth of configuration is visible in the complete example which creates an Aurora PostgreSQL to Aurora MySQL migration with VPC, private subnets, DMS subnet group, security group egress rules for source and target ports, and CloudWatch alarms for CDC latency and full load throughput. The module example demonstrates endpoint definitions with enginename, servername, port, sslmode, extraconnection_attributes, and tags.
Prerequisites remain minimal: Terraform 1.0 or later, an AWS account, source and target database configurations, and understanding of migration requirements. The module supports engine migration, version upgrade, cloud migration, schema migration, and architecture migration patterns through infrastructure as code. Monitoring and schema provisioning can be layered with CloudWatch metric alarms and null_resource provisioners without coupling schema state to Terraform.
The combination of module abstraction and native resources allows teams to standardize DMS deployments, enforce security group and subnet placement, and maintain consistent replication instance settings across environments while retaining the ability to customize endpoints and tasks per migration use case.