Commit 90573a22 authored by Riccardo Salamanna's avatar Riccardo Salamanna Committed by GitHub

feat: allow the creation of cross-region rds replicas (#346)

parent e2636f94
...@@ -251,6 +251,7 @@ Users have the ability to: ...@@ -251,6 +251,7 @@ Users have the ability to:
| <a name="input_create_db_subnet_group"></a> [create\_db\_subnet\_group](#input\_create\_db\_subnet\_group) | Whether to create a database subnet group | `bool` | `true` | no | | <a name="input_create_db_subnet_group"></a> [create\_db\_subnet\_group](#input\_create\_db\_subnet\_group) | Whether to create a database subnet group | `bool` | `true` | no |
| <a name="input_create_monitoring_role"></a> [create\_monitoring\_role](#input\_create\_monitoring\_role) | Create IAM role with a defined name that permits RDS to send enhanced monitoring metrics to CloudWatch Logs. | `bool` | `false` | no | | <a name="input_create_monitoring_role"></a> [create\_monitoring\_role](#input\_create\_monitoring\_role) | Create IAM role with a defined name that permits RDS to send enhanced monitoring metrics to CloudWatch Logs. | `bool` | `false` | no |
| <a name="input_create_random_password"></a> [create\_random\_password](#input\_create\_random\_password) | Whether to create random password for RDS primary cluster | `bool` | `false` | no | | <a name="input_create_random_password"></a> [create\_random\_password](#input\_create\_random\_password) | Whether to create random password for RDS primary cluster | `bool` | `false` | no |
| <a name="input_cross_region_replica"></a> [cross\_region\_replica](#input\_cross\_region\_replica) | Specifies if the replica should be cross region. It allows the use of a subnet group in a region different than the master instance | `bool` | `false` | no |
| <a name="input_db_instance_tags"></a> [db\_instance\_tags](#input\_db\_instance\_tags) | Additional tags for the DB instance | `map(string)` | `{}` | no | | <a name="input_db_instance_tags"></a> [db\_instance\_tags](#input\_db\_instance\_tags) | Additional tags for the DB instance | `map(string)` | `{}` | no |
| <a name="input_db_option_group_tags"></a> [db\_option\_group\_tags](#input\_db\_option\_group\_tags) | Additional tags for the DB option group | `map(string)` | `{}` | no | | <a name="input_db_option_group_tags"></a> [db\_option\_group\_tags](#input\_db\_option\_group\_tags) | Additional tags for the DB option group | `map(string)` | `{}` | no |
| <a name="input_db_parameter_group_tags"></a> [db\_parameter\_group\_tags](#input\_db\_parameter\_group\_tags) | Additional tags for the DB parameter group | `map(string)` | `{}` | no | | <a name="input_db_parameter_group_tags"></a> [db\_parameter\_group\_tags](#input\_db\_parameter\_group\_tags) | Additional tags for the DB parameter group | `map(string)` | `{}` | no |
......
# Master and Replica RDS example for PostgreSQL
Configuration in this directory creates set of RDS resources demonstrating master and replica in the same VPC.
## Usage
To run this example you need to execute:
```bash
$ terraform init
$ terraform plan
$ terraform apply
```
Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources.
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.12.26 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 2.49 |
## Providers
No providers.
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_master"></a> [master](#module\_master) | ../../ | |
| <a name="module_replica"></a> [replica](#module\_replica) | ../../ | |
| <a name="module_security_group_region1"></a> [security\_group\_region1](#module\_security\_group\_region1) | terraform-aws-modules/security-group/aws | ~> 4 |
| <a name="module_security_group_region2"></a> [security\_group\_region2](#module\_security\_group\_region2) | terraform-aws-modules/security-group/aws | ~> 4 |
| <a name="module_vpc_region1"></a> [vpc\_region1](#module\_vpc\_region1) | terraform-aws-modules/vpc/aws | ~> 2.0 |
| <a name="module_vpc_region2"></a> [vpc\_region2](#module\_vpc\_region2) | terraform-aws-modules/vpc/aws | ~> 2.0 |
## Resources
No resources.
## Inputs
No inputs.
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_master_db_instance_address"></a> [master\_db\_instance\_address](#output\_master\_db\_instance\_address) | The address of the RDS instance |
| <a name="output_master_db_instance_arn"></a> [master\_db\_instance\_arn](#output\_master\_db\_instance\_arn) | The ARN of the RDS instance |
| <a name="output_master_db_instance_availability_zone"></a> [master\_db\_instance\_availability\_zone](#output\_master\_db\_instance\_availability\_zone) | The availability zone of the RDS instance |
| <a name="output_master_db_instance_endpoint"></a> [master\_db\_instance\_endpoint](#output\_master\_db\_instance\_endpoint) | The connection endpoint |
| <a name="output_master_db_instance_hosted_zone_id"></a> [master\_db\_instance\_hosted\_zone\_id](#output\_master\_db\_instance\_hosted\_zone\_id) | The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record) |
| <a name="output_master_db_instance_id"></a> [master\_db\_instance\_id](#output\_master\_db\_instance\_id) | The RDS instance ID |
| <a name="output_master_db_instance_name"></a> [master\_db\_instance\_name](#output\_master\_db\_instance\_name) | The database name |
| <a name="output_master_db_instance_password"></a> [master\_db\_instance\_password](#output\_master\_db\_instance\_password) | The database password (this password may be old, because Terraform doesn't track it after initial creation) |
| <a name="output_master_db_instance_port"></a> [master\_db\_instance\_port](#output\_master\_db\_instance\_port) | The database port |
| <a name="output_master_db_instance_resource_id"></a> [master\_db\_instance\_resource\_id](#output\_master\_db\_instance\_resource\_id) | The RDS Resource ID of this instance |
| <a name="output_master_db_instance_status"></a> [master\_db\_instance\_status](#output\_master\_db\_instance\_status) | The RDS instance status |
| <a name="output_master_db_instance_username"></a> [master\_db\_instance\_username](#output\_master\_db\_instance\_username) | The master username for the database |
| <a name="output_master_db_subnet_group_arn"></a> [master\_db\_subnet\_group\_arn](#output\_master\_db\_subnet\_group\_arn) | The ARN of the db subnet group |
| <a name="output_master_db_subnet_group_id"></a> [master\_db\_subnet\_group\_id](#output\_master\_db\_subnet\_group\_id) | The db subnet group name |
| <a name="output_replica_db_instance_address"></a> [replica\_db\_instance\_address](#output\_replica\_db\_instance\_address) | The address of the RDS instance |
| <a name="output_replica_db_instance_arn"></a> [replica\_db\_instance\_arn](#output\_replica\_db\_instance\_arn) | The ARN of the RDS instance |
| <a name="output_replica_db_instance_availability_zone"></a> [replica\_db\_instance\_availability\_zone](#output\_replica\_db\_instance\_availability\_zone) | The availability zone of the RDS instance |
| <a name="output_replica_db_instance_endpoint"></a> [replica\_db\_instance\_endpoint](#output\_replica\_db\_instance\_endpoint) | The connection endpoint |
| <a name="output_replica_db_instance_hosted_zone_id"></a> [replica\_db\_instance\_hosted\_zone\_id](#output\_replica\_db\_instance\_hosted\_zone\_id) | The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record) |
| <a name="output_replica_db_instance_id"></a> [replica\_db\_instance\_id](#output\_replica\_db\_instance\_id) | The RDS instance ID |
| <a name="output_replica_db_instance_name"></a> [replica\_db\_instance\_name](#output\_replica\_db\_instance\_name) | The database name |
| <a name="output_replica_db_instance_port"></a> [replica\_db\_instance\_port](#output\_replica\_db\_instance\_port) | The database port |
| <a name="output_replica_db_instance_resource_id"></a> [replica\_db\_instance\_resource\_id](#output\_replica\_db\_instance\_resource\_id) | The RDS Resource ID of this instance |
| <a name="output_replica_db_instance_status"></a> [replica\_db\_instance\_status](#output\_replica\_db\_instance\_status) | The RDS instance status |
| <a name="output_replica_db_instance_username"></a> [replica\_db\_instance\_username](#output\_replica\_db\_instance\_username) | The replica username for the database |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
provider "aws" {
region = local.region1
}
provider "aws" {
alias = "region2"
region = local.region2
}
locals {
name = "replica-postgresql"
region1 = "eu-west-1"
region2 = "eu-central-1"
tags = {
Owner = "user"
Environment = "dev"
}
engine = "postgres"
engine_version = "11.10"
family = "postgres11" # DB parameter group
major_engine_version = "11" # DB option group
instance_class = "db.t3.large"
allocated_storage = 20
max_allocated_storage = 100
port = 5432
}
################################################################################
# Supporting Resources
################################################################################
module "vpc_region1" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 2.0"
name = local.name
cidr = "10.100.0.0/18"
azs = ["${local.region1}a", "${local.region1}b", "${local.region1}c"]
public_subnets = ["10.100.0.0/24", "10.100.1.0/24", "10.100.2.0/24"]
private_subnets = ["10.100.3.0/24", "10.100.4.0/24", "10.100.5.0/24"]
database_subnets = ["10.100.7.0/24", "10.100.8.0/24", "10.100.9.0/24"]
create_database_subnet_group = true
tags = local.tags
}
module "security_group_region1" {
source = "terraform-aws-modules/security-group/aws"
version = "~> 4"
name = local.name
description = "Replica PostgreSQL example security group"
vpc_id = module.vpc_region1.vpc_id
# ingress
ingress_with_cidr_blocks = [
{
from_port = 5432
to_port = 5432
protocol = "tcp"
description = "PostgreSQL access from within VPC"
cidr_blocks = module.vpc_region1.vpc_cidr_block
},
]
tags = local.tags
}
module "vpc_region2" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 2.0"
providers = {
aws = aws.region2
}
name = local.name
cidr = "10.100.0.0/18"
azs = ["${local.region2}a", "${local.region2}b", "${local.region2}c"]
public_subnets = ["10.100.0.0/24", "10.100.1.0/24", "10.100.2.0/24"]
private_subnets = ["10.100.3.0/24", "10.100.4.0/24", "10.100.5.0/24"]
database_subnets = ["10.100.7.0/24", "10.100.8.0/24", "10.100.9.0/24"]
create_database_subnet_group = true
tags = local.tags
}
module "security_group_region2" {
source = "terraform-aws-modules/security-group/aws"
version = "~> 4"
providers = {
aws = aws.region2
}
name = local.name
description = "Replica PostgreSQL example security group"
vpc_id = module.vpc_region2.vpc_id
# ingress
ingress_with_cidr_blocks = [
{
from_port = 5432
to_port = 5432
protocol = "tcp"
description = "PostgreSQL access from within VPC"
cidr_blocks = module.vpc_region2.vpc_cidr_block
},
]
tags = local.tags
}
################################################################################
# Master DB
################################################################################
module "master" {
source = "../../"
identifier = "${local.name}-master"
engine = local.engine
engine_version = local.engine_version
family = local.family
major_engine_version = local.major_engine_version
instance_class = local.instance_class
allocated_storage = local.allocated_storage
max_allocated_storage = local.max_allocated_storage
storage_encrypted = false
name = "replicaPostgresql"
username = "replica_postgresql"
password = "YourPwdShouldBeLongAndSecure!"
port = local.port
multi_az = true
create_db_subnet_group = false
db_subnet_group_name = module.vpc_region1.database_subnet_group_name
vpc_security_group_ids = [module.security_group_region1.security_group_id]
maintenance_window = "Mon:00:00-Mon:03:00"
backup_window = "03:00-06:00"
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
# Backups are required in order to create a replica
backup_retention_period = 1
skip_final_snapshot = true
deletion_protection = false
tags = local.tags
}
################################################################################
# Replica DB
################################################################################
module "replica" {
source = "../../"
providers = {
aws = aws.region2
}
identifier = "${local.name}-replica"
# Source database. For cross-region use db_instance_arn
replicate_source_db = module.master.db_instance_arn
cross_region_replica = true
engine = local.engine
engine_version = local.engine_version
family = local.family
major_engine_version = local.major_engine_version
instance_class = local.instance_class
allocated_storage = local.allocated_storage
max_allocated_storage = local.max_allocated_storage
storage_encrypted = false
# Username and password should not be set for replicas
username = null
password = null
port = local.port
multi_az = false
vpc_security_group_ids = [module.security_group_region2.security_group_id]
maintenance_window = "Tue:00:00-Tue:03:00"
backup_window = "03:00-06:00"
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
backup_retention_period = 0
skip_final_snapshot = true
deletion_protection = false
# Must create or specify a subnet group since the replica is on another region
create_db_subnet_group = false
db_subnet_group_name = module.vpc_region2.database_subnet_group_name
tags = local.tags
}
# Master
output "master_db_instance_address" {
description = "The address of the RDS instance"
value = module.master.db_instance_address
}
output "master_db_instance_arn" {
description = "The ARN of the RDS instance"
value = module.master.db_instance_arn
}
output "master_db_instance_availability_zone" {
description = "The availability zone of the RDS instance"
value = module.master.db_instance_availability_zone
}
output "master_db_instance_endpoint" {
description = "The connection endpoint"
value = module.master.db_instance_endpoint
}
output "master_db_instance_hosted_zone_id" {
description = "The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record)"
value = module.master.db_instance_hosted_zone_id
}
output "master_db_instance_id" {
description = "The RDS instance ID"
value = module.master.db_instance_id
}
output "master_db_instance_resource_id" {
description = "The RDS Resource ID of this instance"
value = module.master.db_instance_resource_id
}
output "master_db_instance_status" {
description = "The RDS instance status"
value = module.master.db_instance_status
}
output "master_db_instance_name" {
description = "The database name"
value = module.master.db_instance_name
}
output "master_db_instance_username" {
description = "The master username for the database"
value = module.master.db_instance_username
sensitive = true
}
output "master_db_instance_password" {
description = "The database password (this password may be old, because Terraform doesn't track it after initial creation)"
value = module.master.db_instance_password
sensitive = true
}
output "master_db_instance_port" {
description = "The database port"
value = module.master.db_instance_port
}
output "master_db_subnet_group_id" {
description = "The db subnet group name"
value = module.master.db_subnet_group_id
}
output "master_db_subnet_group_arn" {
description = "The ARN of the db subnet group"
value = module.master.db_subnet_group_arn
}
# Replica
output "replica_db_instance_address" {
description = "The address of the RDS instance"
value = module.replica.db_instance_address
}
output "replica_db_instance_arn" {
description = "The ARN of the RDS instance"
value = module.replica.db_instance_arn
}
output "replica_db_instance_availability_zone" {
description = "The availability zone of the RDS instance"
value = module.replica.db_instance_availability_zone
}
output "replica_db_instance_endpoint" {
description = "The connection endpoint"
value = module.replica.db_instance_endpoint
}
output "replica_db_instance_hosted_zone_id" {
description = "The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record)"
value = module.replica.db_instance_hosted_zone_id
}
output "replica_db_instance_id" {
description = "The RDS instance ID"
value = module.replica.db_instance_id
}
output "replica_db_instance_resource_id" {
description = "The RDS Resource ID of this instance"
value = module.replica.db_instance_resource_id
}
output "replica_db_instance_status" {
description = "The RDS instance status"
value = module.replica.db_instance_status
}
output "replica_db_instance_name" {
description = "The database name"
value = module.replica.db_instance_name
}
output "replica_db_instance_username" {
description = "The replica username for the database"
value = module.replica.db_instance_username
sensitive = true
}
output "replica_db_instance_port" {
description = "The database port"
value = module.replica.db_instance_port
}
terraform {
required_version = ">= 0.12.26"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 2.49"
}
}
}
locals { locals {
master_password = var.create_db_instance && var.create_random_password ? random_password.master_password[0].result : var.password master_password = var.create_db_instance && var.create_random_password ? random_password.master_password[0].result : var.password
db_subnet_group_name = var.replicate_source_db != null ? null : coalesce(var.db_subnet_group_name, module.db_subnet_group.db_subnet_group_id, var.identifier) db_subnet_group_name = !var.cross_region_replica && var.replicate_source_db != null ? null : coalesce(var.db_subnet_group_name, module.db_subnet_group.db_subnet_group_id, var.identifier)
parameter_group_name_id = var.create_db_parameter_group ? module.db_parameter_group.db_parameter_group_id : var.parameter_group_name parameter_group_name_id = var.create_db_parameter_group ? module.db_parameter_group.db_parameter_group_id : var.parameter_group_name
......
...@@ -33,6 +33,12 @@ variable "replicate_source_db" { ...@@ -33,6 +33,12 @@ variable "replicate_source_db" {
default = null default = null
} }
variable "cross_region_replica" {
description = "Specifies if the replica should be cross region. It allows the use of a subnet group in a region different than the master instance"
type = bool
default = false
}
variable "license_model" { variable "license_model" {
description = "License model information for this DB instance. Optional, but required for some DB engines, i.e. Oracle SE1" description = "License model information for this DB instance. Optional, but required for some DB engines, i.e. Oracle SE1"
type = string type = string
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment