Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added conditional creation of RDS Aurora cluster #159

Merged
merged 8 commits into from
Oct 30, 2020
32 changes: 24 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ module "db" {
}
```

## Conditional creation

Sometimes you need to have a way to create RDS Aurora resources conditionally but Terraform does not allow to use `count` inside `module` block, so the solution is to specify argument `create_cluster`.

```hcl
# This RDS cluster will not be created
module "db" {
source = "terraform-aws-modules/rds-aurora/aws"
version = "~> 2.0"

create_cluster = false
# ... omitted
}
```

## Examples

- [PostgreSQL](examples/postgresql): A simple example with VPC and PostgreSQL cluster.
Expand All @@ -73,16 +88,16 @@ Terraform documentation is generated automatically using [pre-commit hooks](http

| Name | Version |
|------|---------|
| terraform | >= 0.12.6, < 0.14 |
| aws | >= 2.45, < 4.0 |
| random | ~> 2.2 |
| terraform | >= 0.12.6 |
| aws | >= 2.45 |
| random | >= 2.2 |

## Providers

| Name | Version |
|------|---------|
| aws | >= 2.45, < 4.0 |
| random | ~> 2.2 |
| aws | >= 2.45 |
| random | >= 2.2 |

## Inputs

Expand All @@ -96,6 +111,7 @@ Terraform documentation is generated automatically using [pre-commit hooks](http
| backup\_retention\_period | How long to keep backups for (in days) | `number` | `7` | no |
| ca\_cert\_identifier | The identifier of the CA certificate for the DB instance | `string` | `"rds-ca-2019"` | no |
| copy\_tags\_to\_snapshot | Copy all Cluster tags to snapshots. | `bool` | `false` | no |
| create\_cluster | Controls if RDS cluster should be created (it affects almost all resources) | `bool` | `true` | no |
| create\_monitoring\_role | Whether to create the IAM role for RDS enhanced monitoring | `bool` | `true` | no |
| create\_security\_group | Whether to create security group for RDS cluster | `bool` | `true` | no |
| database\_name | Name for an automatically created database on cluster creation | `string` | `""` | no |
Expand All @@ -112,13 +128,13 @@ Terraform documentation is generated automatically using [pre-commit hooks](http
| global\_cluster\_identifier | The global cluster identifier specified on aws\_rds\_global\_cluster | `string` | `""` | no |
| iam\_database\_authentication\_enabled | Specifies whether IAM Database authentication should be enabled or not. Not all versions and instances are supported. Refer to the AWS documentation to see which versions are supported. | `bool` | `false` | no |
| iam\_roles | A List of ARNs for the IAM roles to associate to the RDS Cluster. | `list(string)` | `[]` | no |
| instance\_type | Instance type to use at master instance. If instance\_type\_replica is not set it will use the same type for replica instances | `string` | n/a | yes |
| instance\_type | Instance type to use at master instance. If instance\_type\_replica is not set it will use the same type for replica instances | `string` | `""` | no |
| instance\_type\_replica | Instance type to use at replica instance | `string` | `null` | no |
| instances\_parameters | Customized instance settings. Supported keys: instance\_name, instance\_type, instance\_promotion\_tier, publicly\_accessible | `list(map(string))` | `[]` | no |
| kms\_key\_id | The ARN for the KMS encryption key if one is set to the cluster. | `string` | `""` | no |
| monitoring\_interval | The interval (seconds) between points when Enhanced Monitoring metrics are collected | `number` | `0` | no |
| monitoring\_role\_arn | IAM role for RDS to send enhanced monitoring metrics to CloudWatch | `string` | `""` | no |
| name | Name given resources | `string` | n/a | yes |
| name | Name given resources | `string` | `""` | no |
| password | Master DB password | `string` | `""` | no |
| performance\_insights\_enabled | Specifies whether Performance Insights is enabled or not. | `bool` | `false` | no |
| performance\_insights\_kms\_key\_id | The ARN for the KMS key to encrypt Performance Insights data. | `string` | `""` | no |
Expand Down Expand Up @@ -146,7 +162,7 @@ Terraform documentation is generated automatically using [pre-commit hooks](http
| subnets | List of subnet IDs to use | `list(string)` | `[]` | no |
| tags | A map of tags to add to all resources. | `map(string)` | `{}` | no |
| username | Master DB username | `string` | `"root"` | no |
| vpc\_id | VPC ID | `string` | n/a | yes |
| vpc\_id | VPC ID | `string` | `""` | no |
| vpc\_security\_group\_ids | List of VPC security groups to associate to the cluster in addition to the SG we create in this module | `list(string)` | `[]` | no |

## Outputs
Expand Down
4 changes: 2 additions & 2 deletions examples/advanced/versions.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
terraform {
required_version = ">= 0.12.6, < 0.14"
required_version = ">= 0.12.6"

required_providers {
aws = ">= 2.45, < 4.0"
aws = ">= 2.45"
}
}
4 changes: 2 additions & 2 deletions examples/custom_instance_settings/versions.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
terraform {
required_version = ">= 0.12.6, < 0.14"
required_version = ">= 0.12.6"

required_providers {
aws = ">= 2.45, < 4.0"
aws = ">= 2.45"
}
}
6 changes: 6 additions & 0 deletions examples/mysql/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,9 @@ resource "aws_iam_policy" "aurora_mysql_policy_iam_auth" {
}
POLICY
}

module "disabled_aurora" {
source = "../../"

create_cluster = false
}
4 changes: 2 additions & 2 deletions examples/mysql/versions.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
terraform {
required_version = ">= 0.12.6, < 0.14"
required_version = ">= 0.12.6"

required_providers {
aws = ">= 2.45, < 4.0"
aws = ">= 2.45"
}
}
4 changes: 2 additions & 2 deletions examples/postgresql/versions.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
terraform {
required_version = ">= 0.12.6, < 0.14"
required_version = ">= 0.12.6"

required_providers {
aws = ">= 2.45, < 4.0"
aws = ">= 2.45"
}
}
4 changes: 2 additions & 2 deletions examples/serverless/versions.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
terraform {
required_version = ">= 0.12.6, < 0.14"
required_version = ">= 0.12.6"

required_providers {
aws = ">= 2.45, < 4.0"
aws = ">= 2.45"
}
}
42 changes: 24 additions & 18 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
locals {
port = var.port == "" ? var.engine == "aurora-postgresql" ? "5432" : "3306" : var.port
master_password = var.password == "" ? random_password.master_password.result : var.password
master_password = var.password == "" ? element(concat(random_password.master_password.*.result, [""]), 0) : var.password
db_subnet_group_name = var.db_subnet_group_name == "" ? join("", aws_db_subnet_group.this.*.name) : var.db_subnet_group_name
backtrack_window = (var.engine == "aurora-mysql" || var.engine == "aurora") && var.engine_mode != "serverless" ? var.backtrack_window : 0

Expand All @@ -14,12 +14,14 @@ locals {

# Random string to use as master password unless one is specified
resource "random_password" "master_password" {
count = var.create_cluster ? 1 : 0

length = 10
special = false
}

resource "aws_db_subnet_group" "this" {
count = var.db_subnet_group_name == "" ? 1 : 0
count = var.create_cluster && var.db_subnet_group_name == "" ? 1 : 0

name = var.name
description = "For Aurora cluster ${var.name}"
Expand All @@ -31,6 +33,8 @@ resource "aws_db_subnet_group" "this" {
}

resource "aws_rds_cluster" "this" {
count = var.create_cluster ? 1 : 0

global_cluster_identifier = var.global_cluster_identifier
cluster_identifier = var.name
replication_source_identifier = var.replication_source_identifier
Expand All @@ -43,7 +47,7 @@ resource "aws_rds_cluster" "this" {
database_name = var.database_name
master_username = var.username
master_password = local.master_password
final_snapshot_identifier = "${var.final_snapshot_identifier_prefix}-${var.name}-${random_id.snapshot_identifier.hex}"
final_snapshot_identifier = "${var.final_snapshot_identifier_prefix}-${var.name}-${element(concat(random_id.snapshot_identifier.*.hex, [""]), 0)}"
skip_final_snapshot = var.skip_final_snapshot
deletion_protection = var.deletion_protection
backup_retention_period = var.backup_retention_period
Expand Down Expand Up @@ -79,10 +83,10 @@ resource "aws_rds_cluster" "this" {
}

resource "aws_rds_cluster_instance" "this" {
count = var.replica_scale_enabled ? var.replica_scale_min : var.replica_count
count = var.create_cluster ? (var.replica_scale_enabled ? var.replica_scale_min : var.replica_count) : 0

identifier = length(var.instances_parameters) > count.index ? lookup(var.instances_parameters[count.index], "instance_name", "${var.name}-${count.index + 1}") : "${var.name}-${count.index + 1}"
cluster_identifier = aws_rds_cluster.this.id
cluster_identifier = element(concat(aws_rds_cluster.this.*.id, [""]), 0)
engine = var.engine
engine_version = var.engine_version
instance_class = length(var.instances_parameters) > count.index ? lookup(var.instances_parameters[count.index], "instance_type", var.instance_type) : count.index > 0 ? coalesce(var.instance_type_replica, var.instance_type) : var.instance_type
Expand Down Expand Up @@ -111,6 +115,8 @@ resource "aws_rds_cluster_instance" "this" {
}

resource "random_id" "snapshot_identifier" {
count = var.create_cluster ? 1 : 0

keepers = {
id = var.name
}
Expand All @@ -130,7 +136,7 @@ data "aws_iam_policy_document" "monitoring_rds_assume_role" {
}

resource "aws_iam_role" "rds_enhanced_monitoring" {
count = var.create_monitoring_role && var.monitoring_interval > 0 ? 1 : 0
count = var.create_cluster && var.create_monitoring_role && var.monitoring_interval > 0 ? 1 : 0

name = "rds-enhanced-monitoring-${var.name}"
assume_role_policy = data.aws_iam_policy_document.monitoring_rds_assume_role.json
Expand All @@ -143,28 +149,28 @@ resource "aws_iam_role" "rds_enhanced_monitoring" {
}

resource "aws_iam_role_policy_attachment" "rds_enhanced_monitoring" {
count = var.create_monitoring_role && var.monitoring_interval > 0 ? 1 : 0
count = var.create_cluster && var.create_monitoring_role && var.monitoring_interval > 0 ? 1 : 0

role = local.rds_enhanced_monitoring_name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"
}

resource "aws_appautoscaling_target" "read_replica_count" {
count = var.replica_scale_enabled ? 1 : 0
count = var.create_cluster && var.replica_scale_enabled ? 1 : 0

max_capacity = var.replica_scale_max
min_capacity = var.replica_scale_min
resource_id = "cluster:${aws_rds_cluster.this.cluster_identifier}"
resource_id = "cluster:${element(concat(aws_rds_cluster.this.*.cluster_identifier, [""]), 0)}"
scalable_dimension = "rds:cluster:ReadReplicaCount"
service_namespace = "rds"
}

resource "aws_appautoscaling_policy" "autoscaling_read_replica_count" {
count = var.replica_scale_enabled ? 1 : 0
count = var.create_cluster && var.replica_scale_enabled ? 1 : 0

name = "target-metric"
policy_type = "TargetTrackingScaling"
resource_id = "cluster:${aws_rds_cluster.this.cluster_identifier}"
resource_id = "cluster:${element(concat(aws_rds_cluster.this.*.cluster_identifier, [""]), 0)}"
scalable_dimension = "rds:cluster:ReadReplicaCount"
service_namespace = "rds"

Expand All @@ -182,7 +188,7 @@ resource "aws_appautoscaling_policy" "autoscaling_read_replica_count" {
}

resource "aws_security_group" "this" {
count = var.create_security_group ? 1 : 0
count = var.create_cluster && var.create_security_group ? 1 : 0

name_prefix = "${var.name}-"
vpc_id = var.vpc_id
Expand All @@ -195,26 +201,26 @@ resource "aws_security_group" "this" {
}

resource "aws_security_group_rule" "default_ingress" {
count = var.create_security_group ? length(var.allowed_security_groups) : 0
count = var.create_cluster && var.create_security_group ? length(var.allowed_security_groups) : 0

description = "From allowed SGs"

type = "ingress"
from_port = aws_rds_cluster.this.port
to_port = aws_rds_cluster.this.port
from_port = element(concat(aws_rds_cluster.this.*.port, [""]), 0)
to_port = element(concat(aws_rds_cluster.this.*.port, [""]), 0)
protocol = "tcp"
source_security_group_id = element(var.allowed_security_groups, count.index)
security_group_id = local.rds_security_group_id
}

resource "aws_security_group_rule" "cidr_ingress" {
count = var.create_security_group && length(var.allowed_cidr_blocks) > 0 ? 1 : 0
count = var.create_cluster && var.create_security_group && length(var.allowed_cidr_blocks) > 0 ? 1 : 0

description = "From allowed CIDRs"

type = "ingress"
from_port = aws_rds_cluster.this.port
to_port = aws_rds_cluster.this.port
from_port = element(concat(aws_rds_cluster.this.*.port, [""]), 0)
to_port = element(concat(aws_rds_cluster.this.*.port, [""]), 0)
protocol = "tcp"
cidr_blocks = var.allowed_cidr_blocks
security_group_id = local.rds_security_group_id
Expand Down
21 changes: 11 additions & 10 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
# aws_rds_cluster
output "this_rds_cluster_arn" {
description = "The ID of the cluster"
value = aws_rds_cluster.this.arn
value = element(concat(aws_rds_cluster.this.*.arn, [""]), 0)
}

output "this_rds_cluster_id" {
description = "The ID of the cluster"
value = aws_rds_cluster.this.id
value = element(concat(aws_rds_cluster.this.*.id, [""]), 0)
}

output "this_rds_cluster_resource_id" {
description = "The Resource ID of the cluster"
value = aws_rds_cluster.this.cluster_resource_id
value = element(concat(aws_rds_cluster.this.*.cluster_resource_id, [""]), 0)
}

output "this_rds_cluster_endpoint" {
description = "The cluster endpoint"
value = aws_rds_cluster.this.endpoint
value = element(concat(aws_rds_cluster.this.*.endpoint, [""]), 0)
}

output "this_rds_cluster_engine_version" {
description = "The cluster engine version"
value = aws_rds_cluster.this.engine_version
value = element(concat(aws_rds_cluster.this.*.engine_version, [""]), 0)
}

output "this_rds_cluster_reader_endpoint" {
description = "The cluster reader endpoint"
value = aws_rds_cluster.this.reader_endpoint
value = element(concat(aws_rds_cluster.this.*.reader_endpoint, [""]), 0)
}

# database_name is not set on `aws_rds_cluster` resource if it was not specified, so can't be used in output
Expand All @@ -37,23 +37,24 @@ output "this_rds_cluster_database_name" {

output "this_rds_cluster_master_password" {
description = "The master password"
value = aws_rds_cluster.this.master_password
value = element(concat(aws_rds_cluster.this.*.master_password, [""]), 0)
sensitive = true
}

output "this_rds_cluster_port" {
description = "The port"
value = aws_rds_cluster.this.port
value = element(concat(aws_rds_cluster.this.*.port, [""]), 0)
}

output "this_rds_cluster_master_username" {
description = "The master username"
value = aws_rds_cluster.this.master_username
value = element(concat(aws_rds_cluster.this.*.master_username, [""]), 0)
}

output "this_rds_cluster_hosted_zone_id" {
description = "Route53 hosted zone id of the created cluster"
value = aws_rds_cluster.this.hosted_zone_id
value = element(concat(aws_rds_cluster.this.*.hosted_zone_id, [""]), 0)

}

# aws_rds_cluster_instance
Expand Down
9 changes: 9 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
variable "create_cluster" {
description = "Controls if RDS cluster should be created (it affects almost all resources)"
type = bool
default = true
}

variable "create_security_group" {
description = "Whether to create security group for RDS cluster"
type = bool
Expand All @@ -7,6 +13,7 @@ variable "create_security_group" {
variable "name" {
description = "Name given resources"
type = string
default = ""
}

variable "subnets" {
Expand Down Expand Up @@ -36,6 +43,7 @@ variable "allowed_cidr_blocks" {
variable "vpc_id" {
description = "VPC ID"
type = string
default = ""
}

variable "instance_type_replica" {
Expand All @@ -47,6 +55,7 @@ variable "instance_type_replica" {
variable "instance_type" {
description = "Instance type to use at master instance. If instance_type_replica is not set it will use the same type for replica instances"
type = string
default = ""
}

variable "publicly_accessible" {
Expand Down
6 changes: 3 additions & 3 deletions versions.tf
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
terraform {
required_version = ">= 0.12.6, < 0.14"
required_version = ">= 0.12.6"

required_providers {
aws = ">= 2.45, < 4.0"
random = "~> 2.2"
aws = ">= 2.45"
random = ">= 2.2"
}
}