Skip to content

Backup module references instance resource before it has been created #732

@OscarVanL

Description

@OscarVanL

TL;DR

I am using a combination of GoogleCloudPlatform/sql-db/google//modules/mysql and GoogleCloudPlatform/sql-db/google//modules/backup to create a MySQL database and setup GCS bucket exports.

I have ran into a scenario where the data "google_sql_database_instance" "backup_instance" data source inside the GoogleCloudPlatform/sql-db/google//modules/backup module references the database instance that has not yet been created, meaning the plan always fails.

Expected behavior

The modules/backup should depend on modules/mysql such that the instance resource has been created before the backups module tries to reference it.

Observed behavior

My plan includes the creation of the database instance (by modules/mysql), but then fails to plan the modules/backup part, as the instance doesn't exist at plan time...

module.mysql.google_sql_database_instance.default will be created
+ resource "google_sql_database_instance" "default" {
      + database_version               = "MYSQL_8_0_40"
      + ip_address                     = (known after apply)
      + name                           = "foo-bar-baz-0e64d9b9"
      ....
}
Plan: 32 to add, 0 to change, 0 to destroy.

╷
│ Error: foo-bar-baz-0e64d9b9 not found
│ 
│   with module.cloudsql_backup.data.google_sql_database_instance.backup_instance,
│   on .terraform/modules/cloudsql_backup/modules/backup/main.tf line 69, in data "google_sql_database_instance" "backup_instance":
│   69: data "google_sql_database_instance" "backup_instance" {

What is so strange about this situation is that the sql_instance name passed to the backup module is module.mysql.instance_name, so terraform should know that the backup module resource creation is dependent on the mysql module.

To provide some additional context about the lead-up to this situation...

  1. I tried to terraform apply, but the apply failed to create the database instance due to a misconfiguration (missing allocated IP range):
    module.mysql.random_id.suffix[0]: Creating...
    module.mysql.null_resource.module_depends_on: Creating...
    module.mysql.null_resource.module_depends_on: Creation complete after 0s [id=1199820259109330906]
    module.mysql.random_id.suffix[0]: Creation complete after 0s [id=C2XYuA]
    module.mysql.google_sql_database_instance.default: Creating...
    Error: Error, failed to create instance foo-bar-baz-0e64d9b9: googleapi: Error 400: Invalid request: Incorrect Service Networking. 
    config for instance: ************:foo-bar-baz-0e64d9b9: Reserved range [[**********]] not found in consumer project:********., invalid
      with module.mysql.google_sql_database_instance.default,
      on .terraform/modules/mysql/modules/mysql/main.tf line 54, in resource "google_sql_database_instance" "default":
      54: resource "google_sql_database_instance" "default" {
    
  2. I fixed the misconfiguration and tried to re-run the plan, but now get the above error even trying to run the plan.

Hypothesis

I have a hypothesis about what the cause is.

In the mysql module, the database name is created from the name and random_id.suffix values in main.tf:

locals {
  master_instance_name     = var.random_instance_name ? "${var.name}-${random_id.suffix[0].hex}" : var.name
  ...
}

resource "google_sql_database_instance" "default" {
  provider             = google-beta
  project              = var.project_id
  name                 = local.master_instance_name
  ...
}

Then in outputs.tf this value is exported, which I reference.

output "instance_name" {
  value       = google_sql_database_instance.default.name
  description = "The instance name for the master instance"
}

Is it possible that since the var.name is known, andrandom_id.suffix resource already got created (module.mysql.random_id.suffix[0]: Creating...), that Terraform is short-circuiting the creation of the google_sql_database_instance, and therefore the instance_name output can be used before the database exists?

Therefore it is possible for me to be in a situation where the "instance_name" output refers to an instance that does not exist.

Terraform Configuration

module "mysql" {
  source        = "GoogleCloudPlatform/sql-db/google//modules/mysql"
  version       = "24.0.1"
  project_id    = var.project_id
  instance_type = "CLOUD_SQL_INSTANCE"

  database_version            = "MYSQL_8_0_40"
  deletion_protection         = var.db_config.deletion_protection
  deletion_protection_enabled = var.db_config.deletion_protection
  disk_autoresize             = true
  disk_size                   = 10
  name                        = local.database_name
  user_name                   = "*********"
  user_password               = var.*********_db_password
  user_host                   = null

  additional_users                = var.additional_users
  iam_users                       = local.iam_users
  zone                            = "*********"
  region                          = "*********"
  tier                            = var.db_config.tier
  edition                         = var.db_config.edition
  enable_default_db               = false
  maintenance_window_day          = 6
  maintenance_window_hour         = 23
  maintenance_window_update_track = var.db_config.maintenance_window_update_track
  random_instance_name            = true
  data_cache_enabled              = false
  ip_configuration = {
    ipv4_enabled                  = false
    authorized_networks           = []
    ssl_mode                      = "TRUSTED_CLIENT_CERTIFICATE_REQUIRED"
    private_network               = data.google_compute_network.network.id
    allocated_ip_range            = var.db_config.allocated_ip_range
    psc_enabled                   = var.db_config.psc_enabled
    psc_allowed_consumer_projects = var.db_config.psc_allowed_consumer_projects
  }
  backup_configuration = {
    enabled                        = true
    location                       = "eu"
    binary_log_enabled             = true
    retained_backups               = 14
    retention_unit                 = "COUNT"
    start_time                     = "00:00"
    transaction_log_retention_days = 7
  }
  insights_config = {
    query_plans_per_minute  = 5     # default
    query_string_length     = 1024  # default
    record_application_tags = false # default
    record_client_address   = false # "Insights record client address is not supported for instances with PSC connectivity enabled"
  }
  database_flags = local.db_flags
}

module "cloudsql_backup" {
  source                 = "GoogleCloudPlatform/sql-db/google//modules/backup"
  version                = "24.0.1"
  export_databases       = [] // exports all databases
  enable_export_backup   = true
  enable_internal_backup = false       // covered by mysql module
  export_schedule        = "0 0 * * *" // at midnight
  export_uri             = google_storage_bucket.*******_export_bucket.url
  region                 = "*******"
  project_id             = var.project_id
  sql_instance           = module.mysql.instance_name
  deletion_protection    = var.backup_deletion_protection
}

Terraform Version

$ terraform version
Terraform v1.10.1

Terraform Provider Versions

terraform providers

Providers required by configuration:
.
├── provider[registry.terraform.io/hashicorp/google]
├── module.primary
│   ├── provider[registry.terraform.io/hashicorp/google]
│   ├── provider[registry.terraform.io/hashicorp/random]
│   ├── module.cloudsql_backup
│   │   └── provider[registry.terraform.io/hashicorp/google] >= 6.11.0, < 7.0.0
│   └── module.mysql
│       ├── provider[registry.terraform.io/hashicorp/google-beta] >= 6.1.0, < 7.0.0
│       ├── provider[registry.terraform.io/hashicorp/null] ~> 3.1
│       ├── provider[registry.terraform.io/hashicorp/random] ~> 3.1
│       └── provider[registry.terraform.io/hashicorp/google] >= 6.1.0, < 7.0.0
└── module.failover
    ├── provider[registry.terraform.io/hashicorp/google]
    ├── provider[registry.terraform.io/hashicorp/random]
    ├── module.cloudsql_backup
        └── provider[registry.terraform.io/hashicorp/google] >= 6.11.0, < 7.0.0
    └── module.mysql
        ├── provider[registry.terraform.io/hashicorp/google] >= 6.1.0, < 7.0.0
        ├── provider[registry.terraform.io/hashicorp/google-beta] >= 6.1.0, < 7.0.0
        ├── provider[registry.terraform.io/hashicorp/null] ~> 3.1
        └── provider[registry.terraform.io/hashicorp/random] ~> 3.1

Providers required by state:

    provider[registry.terraform.io/hashicorp/google]

    provider[registry.terraform.io/hashicorp/google-beta]

    provider[registry.terraform.io/hashicorp/null]

    provider[registry.terraform.io/hashicorp/random]

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions