diff --git a/examples/postgresql-ha/README.md b/examples/postgresql-ha/README.md index 1e440507..0ee2835b 100644 --- a/examples/postgresql-ha/README.md +++ b/examples/postgresql-ha/README.md @@ -23,6 +23,8 @@ terraform destroy | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| folder\_id | The folder where project is created | `string` | n/a | yes | +| key\_project\_id | The project where autokey is setup | `string` | n/a | yes | | pg\_ha\_external\_ip\_range | The ip range to allow connecting from/to Cloud SQL | `string` | `"192.10.10.10/32"` | no | | pg\_ha\_name | The name for Cloud SQL instance | `string` | `"tf-pg-ha"` | no | | project\_id | The project to run tests against | `string` | n/a | yes | diff --git a/examples/postgresql-ha/main.tf b/examples/postgresql-ha/main.tf index 3fbcc667..8417f58b 100644 --- a/examples/postgresql-ha/main.tf +++ b/examples/postgresql-ha/main.tf @@ -48,6 +48,7 @@ module "pg" { maintenance_window_hour = 12 maintenance_window_update_track = "stable" + use_autokey = true deletion_protection = false database_flags = [{ name = "autovacuum", value = "off" }] @@ -127,4 +128,16 @@ module "pg" { random_password = false }, ] + depends_on = [time_sleep.wait_autokey_config] +} + +resource "google_kms_autokey_config" "autokey_config" { + provider = google-beta + folder = var.folder_id + key_project = "projects/${var.key_project_id}" +} + +resource "time_sleep" "wait_autokey_config" { + create_duration = "10s" + depends_on = [google_kms_autokey_config.autokey_config] } diff --git a/examples/postgresql-ha/outputs.tf b/examples/postgresql-ha/outputs.tf index d8cf998d..9459bbff 100644 --- a/examples/postgresql-ha/outputs.tf +++ b/examples/postgresql-ha/outputs.tf @@ -28,9 +28,11 @@ output "authorized_network" { } output "replicas" { - value = module.pg.replicas + value = module.pg.replicas + sensitive = true } output "instances" { - value = module.pg.instances + value = module.pg.instances + sensitive = true } diff --git a/examples/postgresql-ha/variables.tf b/examples/postgresql-ha/variables.tf index 311f951b..9c8ac7b3 100644 --- a/examples/postgresql-ha/variables.tf +++ b/examples/postgresql-ha/variables.tf @@ -30,3 +30,13 @@ variable "pg_ha_external_ip_range" { description = "The ip range to allow connecting from/to Cloud SQL" default = "192.10.10.10/32" } + +variable "key_project_id" { + type = string + description = "The project where autokey is setup" +} + +variable "folder_id" { + type = string + description = "The folder where project is created" +} diff --git a/modules/postgresql/README.md b/modules/postgresql/README.md index 600d709d..b761c0d3 100644 --- a/modules/postgresql/README.md +++ b/modules/postgresql/README.md @@ -170,6 +170,7 @@ module "pg" { | secondary\_zone | The preferred zone for the replica instance, it should be something like: `us-central1-a`, `us-east1-c`. | `string` | `null` | no | | tier | The tier for the Cloud SQL instance. | `string` | `"db-f1-micro"` | no | | update\_timeout | The optional timout that is applied to limit long database updates. | `string` | `"30m"` | no | +| use\_autokey | Enable the use of autokeys from Google Cloud KMS for CMEK. This requires autokey already configured in the project. | `bool` | `false` | no | | user\_deletion\_policy | The deletion policy for the user. Setting ABANDON allows the resource to be abandoned rather than deleted. This is useful for Postgres, where users cannot be deleted from the API if they have been granted SQL roles. Possible values are: "ABANDON". | `string` | `null` | no | | user\_labels | The key/value labels for the Cloud SQL instances. | `map(string)` | `{}` | no | | user\_name | The name of the default user | `string` | `"default"` | no | diff --git a/modules/postgresql/main.tf b/modules/postgresql/main.tf index 4adf4e4d..a70e5526 100644 --- a/modules/postgresql/main.tf +++ b/modules/postgresql/main.tf @@ -45,6 +45,8 @@ locals { connector_enforcement = var.connector_enforcement ? "REQUIRED" : "NOT_REQUIRED" database_name = var.enable_default_db ? var.db_name : (length(var.additional_databases) > 0 ? var.additional_databases[0].name : "") + + encryption_key = var.encryption_key_name != null ? var.encryption_key_name : var.use_autokey ? google_kms_key_handle.default[0].kms_key : null } resource "random_id" "suffix" { @@ -60,7 +62,7 @@ resource "google_sql_database_instance" "default" { database_version = can(regex("\\d", substr(var.database_version, 0, 1))) ? format("POSTGRES_%s", var.database_version) : replace(var.database_version, substr(var.database_version, 0, 8), "POSTGRES") maintenance_version = var.maintenance_version region = var.region - encryption_key_name = var.encryption_key_name + encryption_key_name = local.encryption_key deletion_protection = var.deletion_protection root_password = var.root_password @@ -211,6 +213,15 @@ resource "google_sql_database_instance" "default" { depends_on = [null_resource.module_depends_on] } +resource "google_kms_key_handle" "default" { + count = var.use_autokey ? 1 : 0 + provider = google-beta + project = var.project_id + name = local.instance_name + location = coalesce(var.region, join("-", slice(split("-", var.zone), 0, 2))) + resource_type_selector = "sqladmin.googleapis.com/Instance" +} + resource "google_sql_database" "default" { count = var.enable_default_db ? 1 : 0 name = var.db_name diff --git a/modules/postgresql/variables.tf b/modules/postgresql/variables.tf index 05e44f1d..c3eb59bd 100644 --- a/modules/postgresql/variables.tf +++ b/modules/postgresql/variables.tf @@ -456,3 +456,9 @@ variable "database_integration_roles" { type = list(string) default = [] } + +variable "use_autokey" { + description = "Enable the use of autokeys from Google Cloud KMS for CMEK. This requires autokey already configured in the project." + type = bool + default = false +} diff --git a/test/fixtures/postgresql-ha/main.tf b/test/fixtures/postgresql-ha/main.tf index 7eac8778..9f8e55c4 100644 --- a/test/fixtures/postgresql-ha/main.tf +++ b/test/fixtures/postgresql-ha/main.tf @@ -32,7 +32,6 @@ module "example" { project_id = var.project_id pg_ha_name = var.pg_ha_name pg_ha_external_ip_range = var.pg_ha_external_ip_range + key_project_id = var.key_project_id + folder_id = var.folder_id } - - - diff --git a/test/fixtures/postgresql-ha/variables.tf b/test/fixtures/postgresql-ha/variables.tf index 3ad614b3..8fa1bd92 100644 --- a/test/fixtures/postgresql-ha/variables.tf +++ b/test/fixtures/postgresql-ha/variables.tf @@ -30,3 +30,13 @@ variable "pg_ha_external_ip_range" { description = "The ip range to allow connecting from/to Cloud SQL" default = "192.10.10.10/32" } + +variable "key_project_id" { + type = string + description = "The project where autokey is setup" +} + +variable "folder_id" { + type = string + description = "The folder where project is created" +} diff --git a/test/setup/iam.tf b/test/setup/iam.tf index 745def5f..951be51b 100644 --- a/test/setup/iam.tf +++ b/test/setup/iam.tf @@ -17,6 +17,7 @@ locals { int_required_roles = [ "roles/cloudkms.admin", + "roles/cloudkms.autokeyAdmin", "roles/cloudkms.cryptoKeyEncrypterDecrypter", "roles/cloudscheduler.admin", "roles/cloudsql.admin", @@ -45,6 +46,15 @@ resource "google_project_iam_member" "int_test" { member = "serviceAccount:${google_service_account.int_test.email}" } +resource "google_folder_iam_member" "int_test" { + count = length(local.int_required_roles) + + folder = google_folder.autokey_folder.folder_id + role = local.int_required_roles[count.index] + member = "serviceAccount:${google_service_account.int_test.email}" +} + + resource "google_service_account_key" "int_test" { service_account_id = google_service_account.int_test.id } diff --git a/test/setup/main.tf b/test/setup/main.tf index cf85e563..20f64c75 100644 --- a/test/setup/main.tf +++ b/test/setup/main.tf @@ -16,12 +16,12 @@ module "project" { source = "terraform-google-modules/project-factory/google" - version = "~> 17.0" + version = "~> 18.0" name = "ci-sql-db" random_project_id = "true" org_id = var.org_id - folder_id = var.folder_id + folder_id = google_folder.autokey_folder.folder_id billing_account = var.billing_account deletion_policy = "DELETE" @@ -54,3 +54,56 @@ resource "google_project_service_identity" "workflos_sa" { project = module.project.project_id service = "workflows.googleapis.com" } + +resource "google_folder" "autokey_folder" { + provider = google-beta + display_name = "ci-sql-db-folder" + parent = "folders/${var.folder_id}" + deletion_protection = false +} + +module "autokey-project" { + source = "terraform-google-modules/project-factory/google" + version = "~> 18.0" + + name = "ci-sql-db-autokey" + random_project_id = "true" + org_id = var.org_id + folder_id = google_folder.autokey_folder.folder_id + billing_account = var.billing_account + deletion_policy = "DELETE" + + activate_apis = [ + "cloudkms.googleapis.com", + ] +} + +resource "time_sleep" "wait_enable_service_api" { + depends_on = [module.autokey-project] + create_duration = "30s" +} + +resource "google_project_service_identity" "kms_service_agent" { + provider = google-beta + service = "cloudkms.googleapis.com" + project = module.autokey-project.project_id + depends_on = [time_sleep.wait_enable_service_api] +} + +resource "time_sleep" "wait_service_agent" { + depends_on = [google_project_service_identity.kms_service_agent] + create_duration = "10s" +} + +resource "google_project_iam_member" "autokey_project_admin" { + provider = google-beta + project = module.autokey-project.project_id + role = "roles/cloudkms.admin" + member = "serviceAccount:service-${module.autokey-project.project_number}@gcp-sa-cloudkms.iam.gserviceaccount.com" + depends_on = [time_sleep.wait_service_agent] +} + +resource "time_sleep" "wait_srv_acc_permissions" { + create_duration = "10s" + depends_on = [google_project_iam_member.autokey_project_admin] +} diff --git a/test/setup/outputs.tf b/test/setup/outputs.tf index 00f94d05..fa457d24 100644 --- a/test/setup/outputs.tf +++ b/test/setup/outputs.tf @@ -32,3 +32,11 @@ output "cloudsql_mysql_sa" { value = google_service_account.cloudsql_mysql_sa.email description = "IAM service account user created for Cloud SQL for MySql." } + +output "key_project_id" { + value = module.autokey-project.project_id +} + +output "folder_id" { + value = google_folder.autokey_folder.folder_id +}