From 55c544f3dc89089cf8415f0b7a31d690c17b9c78 Mon Sep 17 00:00:00 2001 From: nitrocode Date: Wed, 7 Jul 2021 17:18:49 -0400 Subject: [PATCH] Allow database user and password to be optional --- main.tf | 85 +++++++++++++++++++++++++++++++++++++++++++++------- outputs.tf | 15 ++++++++++ variables.tf | 44 +++++++++++++++++++++++++-- 3 files changed, 131 insertions(+), 13 deletions(-) diff --git a/main.tf b/main.tf index 8b8614e..2c79786 100644 --- a/main.tf +++ b/main.tf @@ -6,6 +6,8 @@ module "final_snapshot_label" { } locals { + enabled = module.this.enabled + computed_major_engine_version = var.engine == "postgres" ? join(".", slice(split(".", var.engine_version), 0, 1)) : join(".", slice(split(".", var.engine_version), 0, 2)) major_engine_version = var.major_engine_version == "" ? local.computed_major_engine_version : var.major_engine_version @@ -17,15 +19,76 @@ locals { ) availability_zone = var.multi_az ? null : var.availability_zone + + create_user = local.enabled && length(var.database_user) == 0 + create_password = local.enabled && length(var.database_password) == 0 + + ssm_enabled = local.enabled && var.ssm_enabled + ssm_key_prefix = format("/%v/%v", var.ssm_path_prefix, var.database_name) + ssm_key_user = format("%s/%s", local.ssm_key_prefix, "admin/db_user") + ssm_key_user_desc = "RDS Username for the master DB user" + ssm_key_password = format("%s/%s", local.ssm_key_prefix, "admin/db_password") + ssm_key_password_desc = "RDS Password for the master DB user" + + database_user = local.create_user ? join("", random_pet.database_user.*.id) : var.database_user + database_password = local.create_password ? join("", random_password.database_password.*.result) : var.database_password } +resource "random_pet" "database_user" { + count = local.create_user ? 1 : 0 + + # word length + length = 3 + + keepers = { + db_name = var.database_name + } +} + +resource "random_password" "database_password" { + count = local.create_password ? 1 : 0 + + # character length + length = 33 + + # Leave special characters out to avoid quoting and other issues. + # Special characters have no additional security compared to increasing length. + special = false + override_special = "!#$%^&*()<>-_" + + keepers = { + db_name = var.database_name + } +} + +resource "aws_ssm_parameter" "database_user" { + count = local.ssm_enabled ? 1 : 0 + + name = local.ssm_key_user + value = local.database_user + description = local.ssm_key_user_desc + type = "String" + overwrite = true +} + +resource "aws_ssm_parameter" "database_password" { + count = local.ssm_enabled ? 1 : 0 + + name = local.ssm_key_password + value = local.database_password + description = local.ssm_key_password_desc + type = "SecureString" + key_id = var.kms_key_arn + overwrite = true +} + resource "aws_db_instance" "default" { - count = module.this.enabled ? 1 : 0 + count = local.enabled ? 1 : 0 identifier = module.this.id name = var.database_name - username = var.database_user - password = var.database_password + username = local.database_user + password = local.database_password port = var.database_port engine = var.engine engine_version = var.engine_version @@ -91,7 +154,7 @@ resource "aws_db_instance" "default" { } resource "aws_db_parameter_group" "default" { - count = length(var.parameter_group_name) == 0 && module.this.enabled ? 1 : 0 + count = length(var.parameter_group_name) == 0 && local.enabled ? 1 : 0 name_prefix = "${module.this.id}${module.this.delimiter}" family = var.db_parameter_group @@ -112,7 +175,7 @@ resource "aws_db_parameter_group" "default" { } resource "aws_db_option_group" "default" { - count = length(var.option_group_name) == 0 && module.this.enabled ? 1 : 0 + count = length(var.option_group_name) == 0 && local.enabled ? 1 : 0 name_prefix = "${module.this.id}${module.this.delimiter}" engine_name = var.engine @@ -144,7 +207,7 @@ resource "aws_db_option_group" "default" { } resource "aws_db_subnet_group" "default" { - count = module.this.enabled && local.subnet_ids_provided && ! local.db_subnet_group_name_provided ? 1 : 0 + count = local.enabled && local.subnet_ids_provided && ! local.db_subnet_group_name_provided ? 1 : 0 name = module.this.id subnet_ids = var.subnet_ids @@ -152,7 +215,7 @@ resource "aws_db_subnet_group" "default" { } resource "aws_security_group" "default" { - count = module.this.enabled ? 1 : 0 + count = local.enabled ? 1 : 0 name = module.this.id description = "Allow inbound traffic from the security groups" @@ -161,7 +224,7 @@ resource "aws_security_group" "default" { } resource "aws_security_group_rule" "ingress_security_groups" { - count = module.this.enabled ? length(var.security_group_ids) : 0 + count = local.enabled ? length(var.security_group_ids) : 0 description = "Allow inbound traffic from existing Security Groups" type = "ingress" @@ -173,7 +236,7 @@ resource "aws_security_group_rule" "ingress_security_groups" { } resource "aws_security_group_rule" "ingress_cidr_blocks" { - count = module.this.enabled && length(var.allowed_cidr_blocks) > 0 ? 1 : 0 + count = local.enabled && length(var.allowed_cidr_blocks) > 0 ? 1 : 0 description = "Allow inbound traffic from CIDR blocks" type = "ingress" @@ -185,7 +248,7 @@ resource "aws_security_group_rule" "ingress_cidr_blocks" { } resource "aws_security_group_rule" "egress" { - count = module.this.enabled ? 1 : 0 + count = local.enabled ? 1 : 0 description = "Allow all egress traffic" type = "egress" from_port = 0 @@ -199,7 +262,7 @@ module "dns_host_name" { source = "cloudposse/route53-cluster-hostname/aws" version = "0.12.0" - enabled = length(var.dns_zone_id) > 0 && module.this.enabled + enabled = length(var.dns_zone_id) > 0 && local.enabled dns_name = var.host_name zone_id = var.dns_zone_id records = coalescelist(aws_db_instance.default.*.address, [""]) diff --git a/outputs.tf b/outputs.tf index ddbb5a1..b89bf37 100644 --- a/outputs.tf +++ b/outputs.tf @@ -47,3 +47,18 @@ output "resource_id" { value = join("", aws_db_instance.default.*.resource_id) description = "The RDS Resource ID of this instance." } + +output "instance_user" { + value = local.database_user + description = "RDS Username for the master DB user" +} + +output "instance_password_ssm_key" { + value = local.ssm_enabled ? local.ssm_key_password : null + description = "SSM key of RDS password for the master DB user" +} + +output "instance_ssm_key_prefix" { + value = local.ssm_enabled ? local.ssm_key_prefix : null + description = "SSM key prefix of all parameters stored for this instance" +} diff --git a/variables.tf b/variables.tf index 1fc7db4..2901b9d 100644 --- a/variables.tf +++ b/variables.tf @@ -33,16 +33,44 @@ variable "database_name" { description = "The name of the database to create when the DB instance is created" } +# Don't use `admin` +# Read more: +# ("MasterUsername admin cannot be used as it is a reserved word used by the engine") variable "database_user" { type = string - default = "" description = "(Required unless a `snapshot_identifier` or `replicate_source_db` is provided) Username for the master DB user" + default = "" + + validation { + condition = ( + length(var.database_user) == 0 || + (var.database_user != "admin" && + length(var.database_user) >= 1 && + length(var.database_user) <= 16) + ) + error_message = "Per the RDS API, admin cannot be used as it is a reserved word used by the engine. Master username must be between 1 and 16 characters. If null is provided then a random string will be used." + } } +# Must be longer than 8 chars +# Read more: +# ("The parameter MasterUserPassword is not a valid password because it is shorter than 8 characters") variable "database_password" { type = string - default = "" description = "(Required unless a snapshot_identifier or replicate_source_db is provided) Password for the master DB user" + default = "" + + # "sensitive" required Terraform 0.14 or later + # sensitive = true + + validation { + condition = ( + length(var.database_password) == 0 || + (length(var.database_password) >= 8 && + length(var.database_password) <= 128) + ) + error_message = "Per the RDS API, master password must be between 8 and 128 characters. If null is provided then a random password will be used." + } } variable "database_port" { @@ -321,3 +349,15 @@ variable "iam_database_authentication_enabled" { description = "Specifies whether or mappings of AWS Identity and Access Management (IAM) accounts to database accounts is enabled" default = false } + +variable "ssm_enabled" { + type = bool + default = false + description = "If `true` create SSM keys for the database user and password." +} + +variable "ssm_path_prefix" { + type = string + default = "rds" + description = "SSM path prefix (without leading or trailing slash)" +}