Skip to content

Commit

Permalink
feat: initial release of AWS DMS module 🎉
Browse files Browse the repository at this point in the history
  • Loading branch information
bryantbiggs committed Sep 5, 2021
1 parent 92bc2f1 commit d69a806
Show file tree
Hide file tree
Showing 15 changed files with 1,437 additions and 21 deletions.
Binary file added .github/images/dms_complex.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/images/dms_simple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/images/replication_task.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
343 changes: 334 additions & 9 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Terraform <TODO> examples
# Terraform AWS DMS examples

- [Complete](./complete)
53 changes: 46 additions & 7 deletions examples/complete/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Complete <TODO> Example
# Complete AWS DMS Example

Configuration in this directory creates:

- <TODO>
- AWS IAM roles [necessary for AWS DMS](https://aws.amazon.com/premiumsupport/knowledge-center/dms-redshift-connectivity-failures/)
- [AWS DMS subnet group](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_ReplicationInstance.VPC.html)
- [AWS DMS replication instance](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_ReplicationInstance.Creating.html)
- 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
- [AWS DMS replication task](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Tasks.Creating.html)
- 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
- Necessary supporting resources to demonstrate the capabilities of the module (VPC, Aurora clusters, security groups, etc.)

## Usage

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

## Providers

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

## Modules

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

## Resources

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

## Inputs

No inputs.

## Outputs

No outputs.
| Name | Description |
|------|-------------|
| <a name="output_certificates"></a> [certificates](#output\_certificates) | A map of maps containing the certificates created and their full output of attributes and values |
| <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 |
| <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 |
| <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 |
| <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 |
| <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 |
| <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 |
| <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 |
| <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 |
| <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 |
| <a name="output_endpoints"></a> [endpoints](#output\_endpoints) | A map of maps containing the endpoints created and their full output of attributes and values |
| <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 |
| <a name="output_replication_instance_arn"></a> [replication\_instance\_arn](#output\_replication\_instance\_arn) | The Amazon Resource Name (ARN) of the replication instance |
| <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 |
| <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 |
| <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 |
| <a name="output_replication_subnet_group_id"></a> [replication\_subnet\_group\_id](#output\_replication\_subnet\_group\_id) | The ID of the subnet group |
| <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 |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

Apache-2.0 Licensed. See [LICENSE](../../LICENSE).
295 changes: 294 additions & 1 deletion examples/complete/main.tf
Original file line number Diff line number Diff line change
@@ -1 +1,294 @@
locals {}
provider "aws" {
region = local.region
}

locals {
region = "us-east-1"
name = "dms-example-${replace(basename(path.cwd), "_", "-")}"

db_name = "example"
db_username = "example"

# aws dms describe-event-categories
replication_instance_event_categories = ["failure", "creation", "deletion", "maintenance", "failover", "low storage", "configuration change"]
replication_task_event_categories = ["failure", "state change", "creation", "deletion", "configuration change"]

tags = {
Example = local.name
Environment = "dev"
}
}

data "aws_region" "current" {}

################################################################################
# Supporting Resources
################################################################################

module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 3"

name = local.name
cidr = "10.99.0.0/18"

azs = ["${local.region}a", "${local.region}b", "${local.region}d"] # careful on which AZs support DMS VPC endpoint
public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"]
private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"]
database_subnets = ["10.99.7.0/24", "10.99.8.0/24", "10.99.9.0/24"]

create_database_subnet_group = true
enable_nat_gateway = false # not required, using private VPC endpoint
single_nat_gateway = false

enable_dhcp_options = true
enable_dns_hostnames = true
dhcp_options_domain_name = data.aws_region.current.name == "us-east-1" ? "ec2.internal" : "${data.aws_region.current.name}.compute.internal"

tags = local.tags
}

module "vpc_endpoints" {
source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints"
version = "~> 3"

vpc_id = module.vpc.vpc_id
security_group_ids = [module.vpc_endpoint_security_group.security_group_id]

endpoints = {
dms = {
service = "dms"
private_dns_enabled = true
subnet_ids = module.vpc.database_subnets
tags = { Name = "dms-vpc-endpoint" }
}
}

tags = local.tags
}

module "vpc_endpoint_security_group" {
source = "terraform-aws-modules/security-group/aws"
version = "~> 4"

name = "${local.name}-vpc-endpoint"
description = "Security group for VPC endpoints"
vpc_id = module.vpc.vpc_id

ingress_with_cidr_blocks = [
{
from_port = 443
to_port = 443
protocol = "tcp"
description = "VPC Endpoints HTTPs for the VPC CIDR"
cidr_blocks = module.vpc.vpc_cidr_block
}
]

egress_cidr_blocks = [module.vpc.vpc_cidr_block]
egress_rules = ["all-all"]

tags = local.tags
}

module "security_group" {
source = "terraform-aws-modules/security-group/aws"
version = "~> 4"

# Creates multiple
for_each = {
source = ["postgresql-tcp"]
destination = ["mysql-tcp"]
replication-instance = ["postgresql-tcp", "mysql-tcp"]
}

name = "${local.name}-${each.key}"
description = "Security group for ${each.key}"
vpc_id = module.vpc.vpc_id

ingress_cidr_blocks = module.vpc.database_subnets_cidr_blocks
ingress_rules = each.value

egress_cidr_blocks = [module.vpc.vpc_cidr_block]
egress_rules = ["all-all"]

tags = local.tags
}

module "rds_aurora" {
source = "terraform-aws-modules/rds-aurora/aws"
version = "~> 5"

# Creates multiple
for_each = {
source = {
engine = "aurora-postgresql"
engine_version = "11.12"
},
destination = {
engine = "aurora-mysql"
engine_version = "5.7.mysql_aurora.2.07.5"
}
}

name = "${local.name}-${each.key}"
database_name = local.db_name
username = local.db_username
apply_immediately = true

engine = each.value.engine
engine_version = each.value.engine_version
replica_count = 1
instance_type = "db.t3.medium"
storage_encrypted = false
skip_final_snapshot = true

vpc_id = module.vpc.vpc_id
subnets = module.vpc.database_subnets
db_subnet_group_name = module.vpc.database_subnet_group_name
create_security_group = false
vpc_security_group_ids = [module.security_group[each.key].security_group_id]

tags = local.tags
}

resource "aws_sns_topic" "example" {
name = local.name
}

################################################################################
# DMS Module
################################################################################

module "dms_disabled" {
source = "../.."

create = false
}

module "dms_default" {
source = "../.."

# Note - if enabled, this will by default only create
# - DMS necessary IAM roles
# - Subnet group
# - Replication instance
create = false # not enabling by default to avoid messing with the IAM roles

# Subnet group
repl_subnet_group_name = local.name
repl_subnet_group_description = "DMS Subnet group for ${local.name}"
repl_subnet_group_subnet_ids = module.vpc.database_subnets

# Instance
repl_instance_class = "dms.t3.large"
repl_instance_id = local.name

tags = local.tags
}

module "dms_aurora_postgresql_aurora_mysql" {
source = "../.."

# Subnet group
repl_subnet_group_name = local.name
repl_subnet_group_description = "DMS Subnet group for ${local.name}"
repl_subnet_group_subnet_ids = module.vpc.database_subnets

# Instance
repl_instance_allocated_storage = 64
repl_instance_auto_minor_version_upgrade = true
repl_instance_allow_major_version_upgrade = true
repl_instance_apply_immediately = true
repl_instance_engine_version = "3.4.5"
repl_instance_multi_az = true
repl_instance_preferred_maintenance_window = "sun:10:30-sun:14:30"
repl_instance_publicly_accessible = false
repl_instance_class = "dms.t3.large"
repl_instance_id = local.name
repl_instance_vpc_security_group_ids = [module.security_group["replication-instance"].security_group_id]

endpoints = {
source = {
database_name = local.db_name
endpoint_id = "${local.name}-source"
endpoint_type = "source"
engine_name = "aurora-postgresql"
extra_connection_attributes = "heartbeatFrequency=1;"
username = local.db_username
password = module.rds_aurora["source"].rds_cluster_master_password
port = 5432
server_name = module.rds_aurora["source"].rds_cluster_endpoint
ssl_mode = "none"
tags = { EndpointType = "source" }
}

destination = {
database_name = local.db_name
endpoint_id = "${local.name}-destination"
endpoint_type = "target"
engine_name = "aurora"
extra_connection_attributes = ""
username = local.db_username
password = module.rds_aurora["destination"].rds_cluster_master_password
port = 3306
server_name = module.rds_aurora["destination"].rds_cluster_endpoint
ssl_mode = "none"
tags = { EndpointType = "destination" }
}
}

replication_tasks = {
cdc_ex = {
replication_task_id = "${local.name}-cdc"
migration_type = "cdc"
replication_task_settings = file("task_settings.json")
table_mappings = file("table_mappings.json")
source_endpoint_key = "source"
target_endpoint_key = "destination"
tags = { Task = "PostgreSQL-to-MySQL" }
}
}

event_subscriptions = {
# # Despite what the terraform docs say, this is not valid - you must supply a `source_type`
# all = {
# name = "all-events"
# enabled = true
# instance_event_subscription_keys = [local.name]
# task_event_subscription_keys = ["cdc_ex"]
# event_categories = distinct(concat(local.replication_instance_event_categories, local.replication_task_event_categories))
# sns_topic_arn = aws_sns_topic.example.arn
# },
instance = {
name = "instance-events"
enabled = true
instance_event_subscription_keys = [local.name]
source_type = "replication-instance"
event_categories = local.replication_instance_event_categories
sns_topic_arn = aws_sns_topic.example.arn
}
task = {
name = "task-events"
enabled = true
task_event_subscription_keys = ["cdc_ex"]
source_type = "replication-task"
event_categories = local.replication_task_event_categories
sns_topic_arn = aws_sns_topic.example.arn
}
}

# # Not applicable in this example but demonstrating its use
# certificates = {
# source = {
# certificate_id = "${local.name}-source"
# certificate_pem = "..."
# }
# destination = {
# certificate_id = "${local.name}-destination"
# certificate_pem = "..."
# }
# }

tags = local.tags
}
Loading

0 comments on commit d69a806

Please sign in to comment.