Terraform-provider-aws: Question: AWS RDS Resource Recreation

Created on 24 Jan 2019  ·  4Comments  ·  Source: hashicorp/terraform-provider-aws

_This issue was originally opened by @davidgoate as hashicorp/terraform#20088. It was migrated here as a result of the provider split. The original body of the issue is below._


Hi, I have a question relating to AWS RDS cluster and instance creation.

Environment

We recently experimented with:

Terraform v0.11.11
provider.aws v1.41.0

Background

Creating some AWS RDS databases. Our mission was that in some environment (e.g. staging) we may run fewer instances than in others (e.g. production.). With this in mind and not wanting to have totally different terraform files per environment we instead decided to specify the database resources just once and use a variable for the number of instances which is set in our staging.tf and production.tf files respectively for the number of instances.

Potentially one more "quirk" of our setup, is that the VPC in which the subnets exist is not defined in terraform, the VPC already existed via manual creation in the AWS console, so this is provided as a data provider and the subnets for the RDS are specific in terraform - but again this is dynamic in the sense that in some environments we might have 3 subnets (1 in each AZ), whereas in others perhaps we have only 2 subnets. Again to achieve this we used iteration as shown below:

Structure

|-/environments
     -/staging
         -dev.tf
     -/production
         -production.tf
|- /resources
     - database.tf

Example Environment Variables File

  • dev.tf
terraform {
  backend "s3" {
...
  }

  version = "~> 0.11.8"
}

provider "aws" {
...
}

module "main" {
  source                                  = "../../resources"
  vpc_name                                = "test"
  test_db_name                    = "terraform-test-db-dev"
  test_db_instance_count          = 1
  test_db_backup_retention_period = 7
  test_db_backup_window           = "00:57-01:27"
  test_db_maintenance_window      = "tue:04:40-tue:05:10"
  test_db_subnet_count            = 2
  test_db_subnet_cidr_blocks      = ["10.2.4.0/24", "10.2.5.0/24"]
}

Our Issue

Initial resource creation works fine, our subnets are created, the database cluster starts up.

Our issues start the next time we subsequently run a terraform plan or terraform apply, at which point we see interesting things like:

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

module.main.aws_rds_cluster.test_db (new resource required)
id: "terraform-test-db-dev" => (forces new resource)
availability_zones.#: "3" => "1" (forces new resource)
availability_zones.1924028850: "eu-west-1b" => "" (forces new resource)
availability_zones.3953592328: "eu-west-1a" => "eu-west-1a"
availability_zones.94988580: "eu-west-1c" => "" (forces new resource)

and

module.main.aws_rds_cluster_instance.test_db (new resource required)
id: "terraform-test-db-dev" => (forces new resource)
cluster_identifier: "terraform-test-db-dev" => "${aws_rds_cluster.test_db.id}" (forces new resource)

Something about the way we are approaching this appears to be causing terraform to believe that the resource has changed to such an extent that it must destroy the existing resource and create a brand new one.

Config

variable "aws_availability_zones" {
  description = "Run the EC2 Instances in these Availability Zones"
  type        = "list"
  default     = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
}

variable "test_db_name" {
  description = "Name of the RDS instance, must be unique per region and is provided by the module config"
}

variable "test_db_subnet_count" {
  description = "Number of subnets to create, is provided by the module config"
}

resource "aws_security_group" "test_db_service" {
  name   = "${var.test_db_service_user_name}"
  vpc_id = "${data.aws_vpc.vpc.id}"
}

resource "aws_security_group" "test_db" {
  name   = "${var.test_db_name}"
  vpc_id = "${data.aws_vpc.vpc.id}"
}

resource "aws_security_group_rule" "test_db_ingress_app_server" {
  security_group_id        = "${aws_security_group.test_db.id}"
...
  source_security_group_id = "${aws_security_group.test_db_service.id}"
}

variable "test_db_subnet_cidr_blocks" {
  description = "Cidr block allocated to the subnets"
  type        = "list"
}

resource "aws_subnet" "test_db" {
  count             = "${var.test_db_subnet_count}"
  vpc_id            = "${data.aws_vpc.vpc.id}"
  cidr_block        = "${element(var.test_db_subnet_cidr_blocks, count.index)}"
  availability_zone = "${element(var.aws_availability_zones, count.index)}"
}

resource "aws_db_subnet_group" "test_db" {
  name       = "${var.test_db_name}"
  subnet_ids = ["${aws_subnet.test_db.*.id}"]
}

variable "test_db_backup_retention_period" {
  description = "Number of days to keep the backup, is provided by the module config"
}

variable "test_db_backup_window" {
  description = "Window during which the backup is done, is provided by the module config"
}

variable "test_db_maintenance_window" {
  description = "Window during which the maintenance is done, is provided by the module config"
}

data "aws_secretsmanager_secret" "test_db_master_password" {
  name = "terraform/db/test-db/root-password"
}

data "aws_secretsmanager_secret_version" "test_db_master_password" {
  secret_id = "${data.aws_secretsmanager_secret.test_db_master_password.id}"
}

data "aws_iam_role" "rds-monitoring-role" {
  name = "rds-monitoring-role"
}

resource "aws_rds_cluster" "test_db" {
  cluster_identifier = "${var.test_db_name}"
  engine             = "aurora-mysql"
  engine_version     = "5.7.12"

  # can only request to deploy in AZ's where there is a subnet in the subnet group.
  availability_zones              = "${slice(var.aws_availability_zones, 0, var.test_db_instance_count)}"
  database_name                   = "${var.test_db_schema_name}"
  master_username                 = "root"
  master_password                 = "${data.aws_secretsmanager_secret_version.test_db_master_password.secret_string}"
  preferred_backup_window         = "${var.test_db_backup_window}"
  preferred_maintenance_window    = "${var.test_db_maintenance_window}"
  backup_retention_period         = "${var.test_db_backup_retention_period}"
  db_subnet_group_name            = "${aws_db_subnet_group.test_db.name}"
  storage_encrypted               = true
  kms_key_id                      = "${data.aws_kms_key.kms_rds_key.arn}"
  deletion_protection             = true
  enabled_cloudwatch_logs_exports = ["audit", "error", "general", "slowquery"]
  vpc_security_group_ids          = ["${aws_security_group.test_db.id}"]
  final_snapshot_identifier       = "test-db-final-snapshot"
}

variable "test_db_instance_count" {
  description = "Number of instances to create, is provided by the module config"
}

resource "aws_rds_cluster_instance" "test_db" {
  count                = "${var.test_db_instance_count}"
  identifier           = "${var.test_db_name}"
  cluster_identifier   = "${aws_rds_cluster.test_db.id}"
  availability_zone    = "${element(var.aws_availability_zones, count.index)}"
  instance_class       = "db.t2.small"
  db_subnet_group_name = "${aws_db_subnet_group.test_db.name}"
  monitoring_interval  = 60
  engine               = "aurora-mysql"
  engine_version       = "5.7.12"
  monitoring_role_arn  = "${data.aws_iam_role.rds-monitoring-role.arn}"

  tags {
    Name = "test_db-${count.index}"
  }
}

My question is, is there a way to achieve this so that terraform would not try to recreate the resource (e.g. ensure that the availability zones of the cluster and ID of the instance do not change each time we run terraform.

question servicrds

Most helpful comment

Hi @davidgoate 👋 It appears you have discovered one of the fun features of RDS Aurora databases, where it launches instances across 3 availability zones even when supplied fewer than 3.

To account for RDS' behavior here, you have a few options in your Terraform configuration you can use depending on your needs:

  • Removing availability_zones from your resource configuration to allow RDS to automatically manage these (the resource should not report any difference when this is missing)
  • Adding the availability zones reported in the Terraform plan difference to your availability_zones configuration
  • Using ignore_changes on the availability_zones attribute

For more details about these options, please check out https://github.com/terraform-providers/terraform-provider-aws/issues/1111

Does this answer your question?

All 4 comments

Hi @davidgoate 👋 It appears you have discovered one of the fun features of RDS Aurora databases, where it launches instances across 3 availability zones even when supplied fewer than 3.

To account for RDS' behavior here, you have a few options in your Terraform configuration you can use depending on your needs:

  • Removing availability_zones from your resource configuration to allow RDS to automatically manage these (the resource should not report any difference when this is missing)
  • Adding the availability zones reported in the Terraform plan difference to your availability_zones configuration
  • Using ignore_changes on the availability_zones attribute

For more details about these options, please check out https://github.com/terraform-providers/terraform-provider-aws/issues/1111

Does this answer your question?

@bflad thank you for this, I have experimented with just removing the availability zones and I also made a slight change too (which might not actually have been required) to the instance also:

identifier = "${var.test_db_name}-${count.index+1}"
cluster_identifier = "${join("", aws_rds_cluster.test_db.*.id)}"

But it seems the main point was to just remove the explicit mention of availability zones.

Awesome! I'll close this out since I figure the hashibot migration of the issue prevents you from doing so, but reach out with anything else. 👍

I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!

Was this page helpful?
0 / 5 - 0 ratings