Skip to content

Commit d69a806

Browse files
committed
feat: initial release of AWS DMS module 🎉
1 parent 92bc2f1 commit d69a806

File tree

15 files changed

+1437
-21
lines changed

15 files changed

+1437
-21
lines changed

.github/images/dms_complex.png

48.3 KB
Loading

.github/images/dms_simple.png

13.2 KB
Loading
40.7 KB
Loading

README.md

Lines changed: 334 additions & 9 deletions
Large diffs are not rendered by default.

examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
# Terraform <TODO> examples
1+
# Terraform AWS DMS examples
22

33
- [Complete](./complete)

examples/complete/README.md

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
# Complete <TODO> Example
1+
# Complete AWS DMS Example
22

33
Configuration in this directory creates:
44

5-
- <TODO>
5+
- AWS IAM roles [necessary for AWS DMS](https://aws.amazon.com/premiumsupport/knowledge-center/dms-redshift-connectivity-failures/)
6+
- [AWS DMS subnet group](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_ReplicationInstance.VPC.html)
7+
- [AWS DMS replication instance](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_ReplicationInstance.Creating.html)
8+
- Two [AWS DMS replication endpoints](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Endpoints.Creating.html) - one `source` and one `target` to migrate data from an Aurora PostgreSQL cluster to Aurora MySQL cluster
9+
- [AWS DMS replication task](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Tasks.Creating.html)
10+
- Two [AWS DMS event subscriptions](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Events.html) - one for the replication instance, and one for the replication task
11+
- Necessary supporting resources to demonstrate the capabilities of the module (VPC, Aurora clusters, security groups, etc.)
612

713
## Usage
814

@@ -22,27 +28,60 @@ Note that this example may create resources which will incur monetary charges on
2228
| Name | Version |
2329
|------|---------|
2430
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13.1 |
25-
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.30 |
31+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.55 |
2632

2733
## Providers
2834

29-
No providers.
35+
| Name | Version |
36+
|------|---------|
37+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 3.55 |
3038

3139
## Modules
3240

33-
No modules.
41+
| Name | Source | Version |
42+
|------|--------|---------|
43+
| <a name="module_dms_aurora_postgresql_aurora_mysql"></a> [dms\_aurora\_postgresql\_aurora\_mysql](#module\_dms\_aurora\_postgresql\_aurora\_mysql) | ../.. | n/a |
44+
| <a name="module_dms_default"></a> [dms\_default](#module\_dms\_default) | ../.. | n/a |
45+
| <a name="module_dms_disabled"></a> [dms\_disabled](#module\_dms\_disabled) | ../.. | n/a |
46+
| <a name="module_rds_aurora"></a> [rds\_aurora](#module\_rds\_aurora) | terraform-aws-modules/rds-aurora/aws | ~> 5 |
47+
| <a name="module_security_group"></a> [security\_group](#module\_security\_group) | terraform-aws-modules/security-group/aws | ~> 4 |
48+
| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3 |
49+
| <a name="module_vpc_endpoint_security_group"></a> [vpc\_endpoint\_security\_group](#module\_vpc\_endpoint\_security\_group) | terraform-aws-modules/security-group/aws | ~> 4 |
50+
| <a name="module_vpc_endpoints"></a> [vpc\_endpoints](#module\_vpc\_endpoints) | terraform-aws-modules/vpc/aws//modules/vpc-endpoints | ~> 3 |
3451

3552
## Resources
3653

37-
No resources.
54+
| Name | Type |
55+
|------|------|
56+
| [aws_sns_topic.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
57+
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
3858

3959
## Inputs
4060

4161
No inputs.
4262

4363
## Outputs
4464

45-
No outputs.
65+
| Name | Description |
66+
|------|-------------|
67+
| <a name="output_certificates"></a> [certificates](#output\_certificates) | A map of maps containing the certificates created and their full output of attributes and values |
68+
| <a name="output_dms_access_for_endpoint_iam_role_arn"></a> [dms\_access\_for\_endpoint\_iam\_role\_arn](#output\_dms\_access\_for\_endpoint\_iam\_role\_arn) | Amazon Resource Name (ARN) specifying the role |
69+
| <a name="output_dms_access_for_endpoint_iam_role_id"></a> [dms\_access\_for\_endpoint\_iam\_role\_id](#output\_dms\_access\_for\_endpoint\_iam\_role\_id) | Name of the IAM role |
70+
| <a name="output_dms_access_for_endpoint_iam_role_unique_id"></a> [dms\_access\_for\_endpoint\_iam\_role\_unique\_id](#output\_dms\_access\_for\_endpoint\_iam\_role\_unique\_id) | Stable and unique string identifying the role |
71+
| <a name="output_dms_cloudwatch_logs_iam_role_arn"></a> [dms\_cloudwatch\_logs\_iam\_role\_arn](#output\_dms\_cloudwatch\_logs\_iam\_role\_arn) | Amazon Resource Name (ARN) specifying the role |
72+
| <a name="output_dms_cloudwatch_logs_iam_role_id"></a> [dms\_cloudwatch\_logs\_iam\_role\_id](#output\_dms\_cloudwatch\_logs\_iam\_role\_id) | Name of the IAM role |
73+
| <a name="output_dms_cloudwatch_logs_iam_role_unique_id"></a> [dms\_cloudwatch\_logs\_iam\_role\_unique\_id](#output\_dms\_cloudwatch\_logs\_iam\_role\_unique\_id) | Stable and unique string identifying the role |
74+
| <a name="output_dms_vpc_iam_role_arn"></a> [dms\_vpc\_iam\_role\_arn](#output\_dms\_vpc\_iam\_role\_arn) | Amazon Resource Name (ARN) specifying the role |
75+
| <a name="output_dms_vpc_iam_role_id"></a> [dms\_vpc\_iam\_role\_id](#output\_dms\_vpc\_iam\_role\_id) | Name of the IAM role |
76+
| <a name="output_dms_vpc_iam_role_unique_id"></a> [dms\_vpc\_iam\_role\_unique\_id](#output\_dms\_vpc\_iam\_role\_unique\_id) | Stable and unique string identifying the role |
77+
| <a name="output_endpoints"></a> [endpoints](#output\_endpoints) | A map of maps containing the endpoints created and their full output of attributes and values |
78+
| <a name="output_event_subscriptions"></a> [event\_subscriptions](#output\_event\_subscriptions) | A map of maps containing the event subscriptions created and their full output of attributes and values |
79+
| <a name="output_replication_instance_arn"></a> [replication\_instance\_arn](#output\_replication\_instance\_arn) | The Amazon Resource Name (ARN) of the replication instance |
80+
| <a name="output_replication_instance_private_ips"></a> [replication\_instance\_private\_ips](#output\_replication\_instance\_private\_ips) | A list of the private IP addresses of the replication instance |
81+
| <a name="output_replication_instance_public_ips"></a> [replication\_instance\_public\_ips](#output\_replication\_instance\_public\_ips) | A list of the public IP addresses of the replication instance |
82+
| <a name="output_replication_instance_tags_all"></a> [replication\_instance\_tags\_all](#output\_replication\_instance\_tags\_all) | A map of tags assigned to the resource, including those inherited from the provider `default_tags` configuration block |
83+
| <a name="output_replication_subnet_group_id"></a> [replication\_subnet\_group\_id](#output\_replication\_subnet\_group\_id) | The ID of the subnet group |
84+
| <a name="output_replication_tasks"></a> [replication\_tasks](#output\_replication\_tasks) | A map of maps containing the replication tasks created and their full output of attributes and values |
4685
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
4786

4887
Apache-2.0 Licensed. See [LICENSE](../../LICENSE).

examples/complete/main.tf

Lines changed: 294 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,294 @@
1-
locals {}
1+
provider "aws" {
2+
region = local.region
3+
}
4+
5+
locals {
6+
region = "us-east-1"
7+
name = "dms-example-${replace(basename(path.cwd), "_", "-")}"
8+
9+
db_name = "example"
10+
db_username = "example"
11+
12+
# aws dms describe-event-categories
13+
replication_instance_event_categories = ["failure", "creation", "deletion", "maintenance", "failover", "low storage", "configuration change"]
14+
replication_task_event_categories = ["failure", "state change", "creation", "deletion", "configuration change"]
15+
16+
tags = {
17+
Example = local.name
18+
Environment = "dev"
19+
}
20+
}
21+
22+
data "aws_region" "current" {}
23+
24+
################################################################################
25+
# Supporting Resources
26+
################################################################################
27+
28+
module "vpc" {
29+
source = "terraform-aws-modules/vpc/aws"
30+
version = "~> 3"
31+
32+
name = local.name
33+
cidr = "10.99.0.0/18"
34+
35+
azs = ["${local.region}a", "${local.region}b", "${local.region}d"] # careful on which AZs support DMS VPC endpoint
36+
public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"]
37+
private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"]
38+
database_subnets = ["10.99.7.0/24", "10.99.8.0/24", "10.99.9.0/24"]
39+
40+
create_database_subnet_group = true
41+
enable_nat_gateway = false # not required, using private VPC endpoint
42+
single_nat_gateway = false
43+
44+
enable_dhcp_options = true
45+
enable_dns_hostnames = true
46+
dhcp_options_domain_name = data.aws_region.current.name == "us-east-1" ? "ec2.internal" : "${data.aws_region.current.name}.compute.internal"
47+
48+
tags = local.tags
49+
}
50+
51+
module "vpc_endpoints" {
52+
source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints"
53+
version = "~> 3"
54+
55+
vpc_id = module.vpc.vpc_id
56+
security_group_ids = [module.vpc_endpoint_security_group.security_group_id]
57+
58+
endpoints = {
59+
dms = {
60+
service = "dms"
61+
private_dns_enabled = true
62+
subnet_ids = module.vpc.database_subnets
63+
tags = { Name = "dms-vpc-endpoint" }
64+
}
65+
}
66+
67+
tags = local.tags
68+
}
69+
70+
module "vpc_endpoint_security_group" {
71+
source = "terraform-aws-modules/security-group/aws"
72+
version = "~> 4"
73+
74+
name = "${local.name}-vpc-endpoint"
75+
description = "Security group for VPC endpoints"
76+
vpc_id = module.vpc.vpc_id
77+
78+
ingress_with_cidr_blocks = [
79+
{
80+
from_port = 443
81+
to_port = 443
82+
protocol = "tcp"
83+
description = "VPC Endpoints HTTPs for the VPC CIDR"
84+
cidr_blocks = module.vpc.vpc_cidr_block
85+
}
86+
]
87+
88+
egress_cidr_blocks = [module.vpc.vpc_cidr_block]
89+
egress_rules = ["all-all"]
90+
91+
tags = local.tags
92+
}
93+
94+
module "security_group" {
95+
source = "terraform-aws-modules/security-group/aws"
96+
version = "~> 4"
97+
98+
# Creates multiple
99+
for_each = {
100+
source = ["postgresql-tcp"]
101+
destination = ["mysql-tcp"]
102+
replication-instance = ["postgresql-tcp", "mysql-tcp"]
103+
}
104+
105+
name = "${local.name}-${each.key}"
106+
description = "Security group for ${each.key}"
107+
vpc_id = module.vpc.vpc_id
108+
109+
ingress_cidr_blocks = module.vpc.database_subnets_cidr_blocks
110+
ingress_rules = each.value
111+
112+
egress_cidr_blocks = [module.vpc.vpc_cidr_block]
113+
egress_rules = ["all-all"]
114+
115+
tags = local.tags
116+
}
117+
118+
module "rds_aurora" {
119+
source = "terraform-aws-modules/rds-aurora/aws"
120+
version = "~> 5"
121+
122+
# Creates multiple
123+
for_each = {
124+
source = {
125+
engine = "aurora-postgresql"
126+
engine_version = "11.12"
127+
},
128+
destination = {
129+
engine = "aurora-mysql"
130+
engine_version = "5.7.mysql_aurora.2.07.5"
131+
}
132+
}
133+
134+
name = "${local.name}-${each.key}"
135+
database_name = local.db_name
136+
username = local.db_username
137+
apply_immediately = true
138+
139+
engine = each.value.engine
140+
engine_version = each.value.engine_version
141+
replica_count = 1
142+
instance_type = "db.t3.medium"
143+
storage_encrypted = false
144+
skip_final_snapshot = true
145+
146+
vpc_id = module.vpc.vpc_id
147+
subnets = module.vpc.database_subnets
148+
db_subnet_group_name = module.vpc.database_subnet_group_name
149+
create_security_group = false
150+
vpc_security_group_ids = [module.security_group[each.key].security_group_id]
151+
152+
tags = local.tags
153+
}
154+
155+
resource "aws_sns_topic" "example" {
156+
name = local.name
157+
}
158+
159+
################################################################################
160+
# DMS Module
161+
################################################################################
162+
163+
module "dms_disabled" {
164+
source = "../.."
165+
166+
create = false
167+
}
168+
169+
module "dms_default" {
170+
source = "../.."
171+
172+
# Note - if enabled, this will by default only create
173+
# - DMS necessary IAM roles
174+
# - Subnet group
175+
# - Replication instance
176+
create = false # not enabling by default to avoid messing with the IAM roles
177+
178+
# Subnet group
179+
repl_subnet_group_name = local.name
180+
repl_subnet_group_description = "DMS Subnet group for ${local.name}"
181+
repl_subnet_group_subnet_ids = module.vpc.database_subnets
182+
183+
# Instance
184+
repl_instance_class = "dms.t3.large"
185+
repl_instance_id = local.name
186+
187+
tags = local.tags
188+
}
189+
190+
module "dms_aurora_postgresql_aurora_mysql" {
191+
source = "../.."
192+
193+
# Subnet group
194+
repl_subnet_group_name = local.name
195+
repl_subnet_group_description = "DMS Subnet group for ${local.name}"
196+
repl_subnet_group_subnet_ids = module.vpc.database_subnets
197+
198+
# Instance
199+
repl_instance_allocated_storage = 64
200+
repl_instance_auto_minor_version_upgrade = true
201+
repl_instance_allow_major_version_upgrade = true
202+
repl_instance_apply_immediately = true
203+
repl_instance_engine_version = "3.4.5"
204+
repl_instance_multi_az = true
205+
repl_instance_preferred_maintenance_window = "sun:10:30-sun:14:30"
206+
repl_instance_publicly_accessible = false
207+
repl_instance_class = "dms.t3.large"
208+
repl_instance_id = local.name
209+
repl_instance_vpc_security_group_ids = [module.security_group["replication-instance"].security_group_id]
210+
211+
endpoints = {
212+
source = {
213+
database_name = local.db_name
214+
endpoint_id = "${local.name}-source"
215+
endpoint_type = "source"
216+
engine_name = "aurora-postgresql"
217+
extra_connection_attributes = "heartbeatFrequency=1;"
218+
username = local.db_username
219+
password = module.rds_aurora["source"].rds_cluster_master_password
220+
port = 5432
221+
server_name = module.rds_aurora["source"].rds_cluster_endpoint
222+
ssl_mode = "none"
223+
tags = { EndpointType = "source" }
224+
}
225+
226+
destination = {
227+
database_name = local.db_name
228+
endpoint_id = "${local.name}-destination"
229+
endpoint_type = "target"
230+
engine_name = "aurora"
231+
extra_connection_attributes = ""
232+
username = local.db_username
233+
password = module.rds_aurora["destination"].rds_cluster_master_password
234+
port = 3306
235+
server_name = module.rds_aurora["destination"].rds_cluster_endpoint
236+
ssl_mode = "none"
237+
tags = { EndpointType = "destination" }
238+
}
239+
}
240+
241+
replication_tasks = {
242+
cdc_ex = {
243+
replication_task_id = "${local.name}-cdc"
244+
migration_type = "cdc"
245+
replication_task_settings = file("task_settings.json")
246+
table_mappings = file("table_mappings.json")
247+
source_endpoint_key = "source"
248+
target_endpoint_key = "destination"
249+
tags = { Task = "PostgreSQL-to-MySQL" }
250+
}
251+
}
252+
253+
event_subscriptions = {
254+
# # Despite what the terraform docs say, this is not valid - you must supply a `source_type`
255+
# all = {
256+
# name = "all-events"
257+
# enabled = true
258+
# instance_event_subscription_keys = [local.name]
259+
# task_event_subscription_keys = ["cdc_ex"]
260+
# event_categories = distinct(concat(local.replication_instance_event_categories, local.replication_task_event_categories))
261+
# sns_topic_arn = aws_sns_topic.example.arn
262+
# },
263+
instance = {
264+
name = "instance-events"
265+
enabled = true
266+
instance_event_subscription_keys = [local.name]
267+
source_type = "replication-instance"
268+
event_categories = local.replication_instance_event_categories
269+
sns_topic_arn = aws_sns_topic.example.arn
270+
}
271+
task = {
272+
name = "task-events"
273+
enabled = true
274+
task_event_subscription_keys = ["cdc_ex"]
275+
source_type = "replication-task"
276+
event_categories = local.replication_task_event_categories
277+
sns_topic_arn = aws_sns_topic.example.arn
278+
}
279+
}
280+
281+
# # Not applicable in this example but demonstrating its use
282+
# certificates = {
283+
# source = {
284+
# certificate_id = "${local.name}-source"
285+
# certificate_pem = "..."
286+
# }
287+
# destination = {
288+
# certificate_id = "${local.name}-destination"
289+
# certificate_pem = "..."
290+
# }
291+
# }
292+
293+
tags = local.tags
294+
}

0 commit comments

Comments
 (0)