From de24cda3a1ebb7b495970d565a6e0e01c5c13368 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Wed, 17 Feb 2021 20:40:18 +0000 Subject: [PATCH] Add EncryptionConfig to Cloud Composer's EnvironmentConfig (#4310) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adds a new section (EncryptionConfig) to Cloud Composer's EnvironmentConfig used for environment creation * Add missing part of the config. * Fixes to how the kms key is passed * Fix testing call * Fix nesting of ForceNew in encryption_config Co-authored-by: Aleksandra JarmoliƄska Signed-off-by: Modular Magician --- .changelog/4310.txt | 3 + google-beta/resource_composer_environment.go | 51 +++++++++ .../resource_composer_environment_test.go | 101 ++++++++++++++++++ .../docs/r/composer_environment.html.markdown | 11 ++ 4 files changed, 166 insertions(+) create mode 100644 .changelog/4310.txt diff --git a/.changelog/4310.txt b/.changelog/4310.txt new file mode 100644 index 0000000000..384424dee1 --- /dev/null +++ b/.changelog/4310.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +composer: added `encryption_config` to `google_composer_environment` resource (TPGB only) +``` diff --git a/google-beta/resource_composer_environment.go b/google-beta/resource_composer_environment.go index b255695798..325ccc16a9 100644 --- a/google-beta/resource_composer_environment.go +++ b/google-beta/resource_composer_environment.go @@ -52,6 +52,7 @@ var ( "config.0.web_server_network_access_control", "config.0.database_config", "config.0.web_server_config", + "config.0.encryption_config", } allowedIpRangesConfig = &schema.Resource{ @@ -422,6 +423,24 @@ func resourceComposerEnvironment() *schema.Resource { }, }, }, + "encryption_config": { + Type: schema.TypeList, + Optional: true, + Computed: true, + AtLeastOneOf: composerConfigKeys, + MaxItems: 1, + Description: `The encryption options for the Composer environment and its dependencies.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kms_key_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Optional. Customer-managed Encryption Key available through Google's Key Management Service. Cannot be updated.`, + }, + }, + }, + }, "airflow_uri": { Type: schema.TypeString, Computed: true, @@ -809,6 +828,7 @@ func flattenComposerEnvironmentConfig(envCfg *composer.EnvironmentConfig) interf transformed["web_server_network_access_control"] = flattenComposerEnvironmentConfigWebServerNetworkAccessControl(envCfg.WebServerNetworkAccessControl) transformed["database_config"] = flattenComposerEnvironmentConfigDatabaseConfig(envCfg.DatabaseConfig) transformed["web_server_config"] = flattenComposerEnvironmentConfigWebServerConfig(envCfg.WebServerConfig) + transformed["encryption_config"] = flattenComposerEnvironmentConfigEncryptionConfig(envCfg.EncryptionConfig) return []interface{}{transformed} } @@ -856,6 +876,17 @@ func flattenComposerEnvironmentConfigWebServerConfig(webServerCfg *composer.WebS return []interface{}{transformed} } +func flattenComposerEnvironmentConfigEncryptionConfig(encryptionCfg *composer.EncryptionConfig) interface{} { + if encryptionCfg == nil { + return nil + } + + transformed := make(map[string]interface{}) + transformed["kms_key_name"] = encryptionCfg.KmsKeyName + + return []interface{}{transformed} +} + func flattenComposerEnvironmentConfigPrivateEnvironmentConfig(envCfg *composer.PrivateEnvironmentConfig) interface{} { if envCfg == nil { return nil @@ -980,6 +1011,12 @@ func expandComposerEnvironmentConfig(v interface{}, d *schema.ResourceData, conf } transformed.WebServerConfig = transformedWebServerConfig + transformedEncryptionConfig, err := expandComposerEnvironmentConfigEncryptionConfig(original["encryption_config"], d, config) + if err != nil { + return nil, err + } + transformed.EncryptionConfig = transformedEncryptionConfig + return transformed, nil } @@ -1047,6 +1084,20 @@ func expandComposerEnvironmentConfigWebServerConfig(v interface{}, d *schema.Res return transformed, nil } +func expandComposerEnvironmentConfigEncryptionConfig(v interface{}, d *schema.ResourceData, config *Config) (*composer.EncryptionConfig, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + + transformed := &composer.EncryptionConfig{} + transformed.KmsKeyName = original["kms_key_name"].(string) + + return transformed, nil +} + func expandComposerEnvironmentConfigPrivateEnvironmentConfig(v interface{}, d *schema.ResourceData, config *Config) (*composer.PrivateEnvironmentConfig, error) { l := v.([]interface{}) if len(l) == 0 { diff --git a/google-beta/resource_composer_environment_test.go b/google-beta/resource_composer_environment_test.go index 9279d1462b..66a8273436 100644 --- a/google-beta/resource_composer_environment_test.go +++ b/google-beta/resource_composer_environment_test.go @@ -290,6 +290,41 @@ func TestAccComposerEnvironment_withWebServerConfig(t *testing.T) { }) } +func TestAccComposerEnvironment_withEncryptionConfig(t *testing.T) { + t.Parallel() + + kms := BootstrapKMSKeyInLocation(t, "us-central1") + pid := getTestProjectFromEnv() + envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, randInt(t)) + network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, randInt(t)) + subnetwork := network + "-1" + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComposerEnvironment_encryptionCfg(pid, envName, kms.CryptoKey.Name, network, subnetwork), + }, + { + ResourceName: "google_composer_environment.test", + ImportState: true, + ImportStateVerify: true, + }, + // This is a terrible clean-up step in order to get destroy to succeed, + // due to dangling firewall rules left by the Composer Environment blocking network deletion. + // TODO(dzarmola): Remove this check if firewall rules bug gets fixed by Composer. + { + PlanOnly: true, + ExpectNonEmptyPlan: false, + Config: testAccComposerEnvironment_encryptionCfg(pid, envName, kms.CryptoKey.Name, network, subnetwork), + Check: testAccCheckClearComposerEnvironmentFirewalls(t, network), + }, + }, + }) +} + // Checks behavior of node config, including dependencies on Compute resources. func TestAccComposerEnvironment_withNodeConfig(t *testing.T) { t.Parallel() @@ -726,6 +761,72 @@ resource "google_compute_subnetwork" "test" { `, name, network, subnetwork) } +func testAccComposerEnvironment_encryptionCfg(pid, name, kmsKey, network, subnetwork string) string { + return fmt.Sprintf(` +data "google_project" "project" { + project_id = "%s" +} +resource "google_project_iam_member" "kms-project-binding1" { + project = data.google_project.project.project_id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project.number}@cloudcomposer-accounts.iam.gserviceaccount.com" +} +resource "google_project_iam_member" "kms-project-binding2" { + project = data.google_project.project.project_id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project.number}@compute-system.iam.gserviceaccount.com" +} +resource "google_project_iam_member" "kms-project-binding3" { + project = data.google_project.project.project_id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" +} +resource "google_project_iam_member" "kms-project-binding4" { + project = data.google_project.project.project_id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com" +} +resource "google_project_iam_member" "kms-project-binding5" { + project = data.google_project.project.project_id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-pubsub.iam.gserviceaccount.com" +} +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = "%s" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project.number}@gs-project-accounts.iam.gserviceaccount.com" +} +resource "google_composer_environment" "test" { + depends_on = [google_project_iam_member.kms-project-binding1, google_project_iam_member.kms-project-binding2, + google_project_iam_member.kms-project-binding3, google_project_iam_member.kms-project-binding4, + google_project_iam_member.kms-project-binding5, google_kms_crypto_key_iam_member.iam] + name = "%s" + region = "us-central1" + config { + node_config { + network = google_compute_network.test.self_link + subnetwork = google_compute_subnetwork.test.self_link + zone = "us-central1-a" + } + encryption_config { + kms_key_name = "%s" + } + } +} +// use a separate network to avoid conflicts with other tests running in parallel +// that use the default network/subnet +resource "google_compute_network" "test" { + name = "%s" + auto_create_subnetworks = false +} +resource "google_compute_subnetwork" "test" { + name = "%s" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = google_compute_network.test.self_link +} +`, pid, kmsKey, name, kmsKey, network, subnetwork) +} func testAccComposerEnvironment_update(name, network, subnetwork string) string { return fmt.Sprintf(` data "google_composer_image_versions" "all" { diff --git a/website/docs/r/composer_environment.html.markdown b/website/docs/r/composer_environment.html.markdown index ae159d2c31..b83cb6ca4b 100644 --- a/website/docs/r/composer_environment.html.markdown +++ b/website/docs/r/composer_environment.html.markdown @@ -179,6 +179,10 @@ The `config` block supports: (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) The configuration settings for the Airflow web server App Engine instance. +* `encryption_config` - + (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) + The encryption options for the Cloud Composer environment and its dependencies. + The `node_config` block supports: * `zone` - @@ -390,6 +394,13 @@ The `web_server_config` block supports: Value custom is returned only in response, if Airflow web server parameters were manually changed to a non-standard values. +The `encryption_config` block supports: + +* `kms_key_name` - + (Required) + Customer-managed Encryption Key available through Google's Key Management Service. It must + be the fully qualified resource name, + i.e. projects/project-id/locations/location/keyRings/keyring/cryptoKeys/key. Cannot be updated. ## Attributes Reference