diff --git a/.changelog/7781.txt b/.changelog/7781.txt new file mode 100644 index 0000000000..6d35002c85 --- /dev/null +++ b/.changelog/7781.txt @@ -0,0 +1,6 @@ +```release-note:enhancement +alloydb: added `encryption_config` and `encryption_info` fields in `google_alloydb_cluster`, to allow CMEK encryption of the cluster's data. +``` +```release-note:enhancement +alloydb: added the `encryption_config` field inside the `automated_backup_policy` block in`google_alloydb_cluster`, to allow CMEK encryption of automated backups. +``` diff --git a/google-beta/resource_alloydb_cluster.go b/google-beta/resource_alloydb_cluster.go index 4cf70f9218..77cbe9729e 100644 --- a/google-beta/resource_alloydb_cluster.go +++ b/google-beta/resource_alloydb_cluster.go @@ -87,6 +87,21 @@ A duration in seconds with up to nine fractional digits, terminated by 's'. Exam Optional: true, Description: `Whether automated backups are enabled.`, }, + "encryption_config": { + Type: schema.TypeList, + Optional: true, + Description: `EncryptionConfig describes the encryption config of a cluster or a backup that is encrypted with a CMEK (customer-managed encryption key).`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kms_key_name": { + Type: schema.TypeString, + Optional: true, + Description: `The fully-qualified resource name of the KMS key. Each Cloud KMS key is regionalized and has the following format: projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME].`, + }, + }, + }, + }, "labels": { Type: schema.TypeMap, Optional: true, @@ -189,6 +204,22 @@ A duration in seconds with up to nine fractional digits, terminated by 's'. Exam Optional: true, Description: `User-settable and human-readable display name for the Cluster.`, }, + "encryption_config": { + Type: schema.TypeList, + Optional: true, + Description: `EncryptionConfig describes the encryption config of a cluster or a backup that is encrypted with a CMEK (customer-managed encryption key).`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kms_key_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `The fully-qualified resource name of the KMS key. Each Cloud KMS key is regionalized and has the following format: projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME].`, + }, + }, + }, + }, "initial_user": { Type: schema.TypeList, Optional: true, @@ -235,6 +266,28 @@ A duration in seconds with up to nine fractional digits, terminated by 's'. Exam Computed: true, Description: `The database engine major version. This is an output-only field and it's populated at the Cluster creation time. This field cannot be changed after cluster creation.`, }, + "encryption_info": { + Type: schema.TypeList, + Computed: true, + Description: `EncryptionInfo describes the encryption information of a cluster or a backup.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "encryption_type": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. Type of encryption.`, + }, + "kms_key_versions": { + Type: schema.TypeList, + Computed: true, + Description: `Output only. Cloud KMS key versions that are being used to protect the database or the backup.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, "migration_source": { Type: schema.TypeList, Computed: true, @@ -294,6 +347,12 @@ func resourceAlloydbClusterCreate(d *schema.ResourceData, meta interface{}) erro } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { obj["labels"] = labelsProp } + encryptionConfigProp, err := expandAlloydbClusterEncryptionConfig(d.Get("encryption_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("encryption_config"); !isEmptyValue(reflect.ValueOf(encryptionConfigProp)) && (ok || !reflect.DeepEqual(v, encryptionConfigProp)) { + obj["encryptionConfig"] = encryptionConfigProp + } networkProp, err := expandAlloydbClusterNetwork(d.Get("network"), d, config) if err != nil { return err @@ -408,6 +467,12 @@ func resourceAlloydbClusterRead(d *schema.ResourceData, meta interface{}) error if err := d.Set("labels", flattenAlloydbClusterLabels(res["labels"], d, config)); err != nil { return fmt.Errorf("Error reading Cluster: %s", err) } + if err := d.Set("encryption_config", flattenAlloydbClusterEncryptionConfig(res["encryptionConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading Cluster: %s", err) + } + if err := d.Set("encryption_info", flattenAlloydbClusterEncryptionInfo(res["encryptionInfo"], d, config)); err != nil { + return fmt.Errorf("Error reading Cluster: %s", err) + } if err := d.Set("network", flattenAlloydbClusterNetwork(res["network"], d, config)); err != nil { return fmt.Errorf("Error reading Cluster: %s", err) } @@ -452,6 +517,12 @@ func resourceAlloydbClusterUpdate(d *schema.ResourceData, meta interface{}) erro } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { obj["labels"] = labelsProp } + encryptionConfigProp, err := expandAlloydbClusterEncryptionConfig(d.Get("encryption_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("encryption_config"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, encryptionConfigProp)) { + obj["encryptionConfig"] = encryptionConfigProp + } networkProp, err := expandAlloydbClusterNetwork(d.Get("network"), d, config) if err != nil { return err @@ -489,6 +560,10 @@ func resourceAlloydbClusterUpdate(d *schema.ResourceData, meta interface{}) erro updateMask = append(updateMask, "labels") } + if d.HasChange("encryption_config") { + updateMask = append(updateMask, "encryptionConfig") + } + if d.HasChange("network") { updateMask = append(updateMask, "network") } @@ -613,6 +688,46 @@ func flattenAlloydbClusterLabels(v interface{}, d *schema.ResourceData, config * return v } +func flattenAlloydbClusterEncryptionConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["kms_key_name"] = + flattenAlloydbClusterEncryptionConfigKmsKeyName(original["kmsKeyName"], d, config) + return []interface{}{transformed} +} +func flattenAlloydbClusterEncryptionConfigKmsKeyName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenAlloydbClusterEncryptionInfo(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["encryption_type"] = + flattenAlloydbClusterEncryptionInfoEncryptionType(original["encryptionType"], d, config) + transformed["kms_key_versions"] = + flattenAlloydbClusterEncryptionInfoKmsKeyVersions(original["kmsKeyVersions"], d, config) + return []interface{}{transformed} +} +func flattenAlloydbClusterEncryptionInfoEncryptionType(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenAlloydbClusterEncryptionInfoKmsKeyVersions(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + func flattenAlloydbClusterNetwork(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { return v } @@ -640,6 +755,8 @@ func flattenAlloydbClusterAutomatedBackupPolicy(v interface{}, d *schema.Resourc flattenAlloydbClusterAutomatedBackupPolicyLocation(original["location"], d, config) transformed["labels"] = flattenAlloydbClusterAutomatedBackupPolicyLabels(original["labels"], d, config) + transformed["encryption_config"] = + flattenAlloydbClusterAutomatedBackupPolicyEncryptionConfig(original["encryptionConfig"], d, config) transformed["weekly_schedule"] = flattenAlloydbClusterAutomatedBackupPolicyWeeklySchedule(original["weeklySchedule"], d, config) transformed["time_based_retention"] = @@ -662,6 +779,23 @@ func flattenAlloydbClusterAutomatedBackupPolicyLabels(v interface{}, d *schema.R return v } +func flattenAlloydbClusterAutomatedBackupPolicyEncryptionConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["kms_key_name"] = + flattenAlloydbClusterAutomatedBackupPolicyEncryptionConfigKmsKeyName(original["kmsKeyName"], d, config) + return []interface{}{transformed} +} +func flattenAlloydbClusterAutomatedBackupPolicyEncryptionConfigKmsKeyName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + func flattenAlloydbClusterAutomatedBackupPolicyWeeklySchedule(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { if v == nil { return nil @@ -878,6 +1012,29 @@ func expandAlloydbClusterLabels(v interface{}, d TerraformResourceData, config * return m, nil } +func expandAlloydbClusterEncryptionConfig(v interface{}, d TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedKmsKeyName, err := expandAlloydbClusterEncryptionConfigKmsKeyName(original["kms_key_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKmsKeyName); val.IsValid() && !isEmptyValue(val) { + transformed["kmsKeyName"] = transformedKmsKeyName + } + + return transformed, nil +} + +func expandAlloydbClusterEncryptionConfigKmsKeyName(v interface{}, d TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + func expandAlloydbClusterNetwork(v interface{}, d TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { return v, nil } @@ -950,6 +1107,13 @@ func expandAlloydbClusterAutomatedBackupPolicy(v interface{}, d TerraformResourc transformed["labels"] = transformedLabels } + transformedEncryptionConfig, err := expandAlloydbClusterAutomatedBackupPolicyEncryptionConfig(original["encryption_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEncryptionConfig); val.IsValid() && !isEmptyValue(val) { + transformed["encryptionConfig"] = transformedEncryptionConfig + } + transformedWeeklySchedule, err := expandAlloydbClusterAutomatedBackupPolicyWeeklySchedule(original["weekly_schedule"], d, config) if err != nil { return nil, err @@ -1000,6 +1164,29 @@ func expandAlloydbClusterAutomatedBackupPolicyLabels(v interface{}, d TerraformR return m, nil } +func expandAlloydbClusterAutomatedBackupPolicyEncryptionConfig(v interface{}, d TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedKmsKeyName, err := expandAlloydbClusterAutomatedBackupPolicyEncryptionConfigKmsKeyName(original["kms_key_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKmsKeyName); val.IsValid() && !isEmptyValue(val) { + transformed["kmsKeyName"] = transformedKmsKeyName + } + + return transformed, nil +} + +func expandAlloydbClusterAutomatedBackupPolicyEncryptionConfigKmsKeyName(v interface{}, d TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + func expandAlloydbClusterAutomatedBackupPolicyWeeklySchedule(v interface{}, d TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { l := v.([]interface{}) if len(l) == 0 || l[0] == nil { diff --git a/google-beta/resource_alloydb_cluster_test.go b/google-beta/resource_alloydb_cluster_test.go index c1b2d57869..061a368f2a 100644 --- a/google-beta/resource_alloydb_cluster_test.go +++ b/google-beta/resource_alloydb_cluster_test.go @@ -460,3 +460,284 @@ resource "google_compute_network" "default" { } `, context) } +func TestAccAlloydbCluster_usingCMEK(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": RandString(t, 10), + "key_name": "tf-test-key-" + RandString(t, 10), + } + + VcrTest(t, resource.TestCase{ + PreCheck: func() { AccTestPreCheck(t) }, + ProtoV5ProviderFactories: ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckAlloydbClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccAlloydbCluster_usingCMEK(context), + }, + { + ResourceName: "google_alloydb_cluster.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"cluster_id", "location"}, + }, + }, + }) +} + +func testAccAlloydbCluster_usingCMEK(context map[string]interface{}) string { + return Nprintf(` +resource "google_alloydb_cluster" "default" { + cluster_id = "tf-test-alloydb-cluster%{random_suffix}" + location = "us-central1" + network = "projects/${data.google_project.project.number}/global/networks/${google_compute_network.default.name}" + encryption_config { + kms_key_name = google_kms_crypto_key.key.id + } + depends_on = [google_kms_crypto_key_iam_binding.crypto_key] +} +resource "google_compute_network" "default" { + name = "tf-test-alloydb-cluster%{random_suffix}" +} +data "google_project" "project" {} +resource "google_kms_key_ring" "keyring" { + name = "%{key_name}" + location = "us-central1" +} +resource "google_kms_crypto_key" "key" { + name = "%{key_name}" + key_ring = google_kms_key_ring.keyring.id +} +resource "google_kms_crypto_key_iam_binding" "crypto_key" { + crypto_key_id = google_kms_crypto_key.key.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + members = [ + "serviceAccount:service-${data.google_project.project.number}@gcp-sa-alloydb.iam.gserviceaccount.com", + ] +} +`, context) +} + +func TestAccAlloydbCluster_CMEKInAutomatedBackupIsUpdatable(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": RandString(t, 10), + "key_name": "tf-test-key-" + RandString(t, 10), + } + + VcrTest(t, resource.TestCase{ + PreCheck: func() { AccTestPreCheck(t) }, + ProtoV5ProviderFactories: ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckAlloydbClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccAlloydbCluster_usingCMEKInClusterAndAutomatedBackup(context), + }, + { + ResourceName: "google_alloydb_cluster.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"cluster_id", "location"}, + }, + { + Config: testAccAlloydbCluster_updateCMEKInAutomatedBackup(context), + }, + { + ResourceName: "google_alloydb_cluster.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"cluster_id", "location"}, + }, + { + Config: testAccAlloydbCluster_usingCMEKallowDeletion(context), + }, + { + ResourceName: "google_alloydb_cluster.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"cluster_id", "location"}, + }, + }, + }) +} + +func testAccAlloydbCluster_usingCMEKInClusterAndAutomatedBackup(context map[string]interface{}) string { + return Nprintf(` +resource "google_alloydb_cluster" "default" { + cluster_id = "tf-test-alloydb-cluster%{random_suffix}" + location = "us-central1" + network = "projects/${data.google_project.project.number}/global/networks/${google_compute_network.default.name}" + encryption_config { + kms_key_name = google_kms_crypto_key.key.id + } + automated_backup_policy { + location = "us-central1" + backup_window = "1800s" + enabled = true + encryption_config { + kms_key_name = google_kms_crypto_key.key.id + } + time_based_retention { + retention_period = "510s" + } + } + lifecycle { + prevent_destroy = true + } + depends_on = [google_kms_crypto_key_iam_binding.crypto_key] +} + +resource "google_compute_network" "default" { + name = "tf-test-alloydb-cluster%{random_suffix}" +} + +data "google_project" "project" {} + +resource "google_kms_key_ring" "keyring" { + name = "%{key_name}" + location = "us-central1" +} + +resource "google_kms_crypto_key" "key" { + name = "%{key_name}" + key_ring = google_kms_key_ring.keyring.id +} + +resource "google_kms_crypto_key_iam_binding" "crypto_key" { + crypto_key_id = google_kms_crypto_key.key.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + members = [ + "serviceAccount:service-${data.google_project.project.number}@gcp-sa-alloydb.iam.gserviceaccount.com", + ] +} +`, context) +} + +func testAccAlloydbCluster_updateCMEKInAutomatedBackup(context map[string]interface{}) string { + return Nprintf(` +resource "google_alloydb_cluster" "default" { + cluster_id = "tf-test-alloydb-cluster%{random_suffix}" + location = "us-central1" + network = "projects/${data.google_project.project.number}/global/networks/${google_compute_network.default.name}" + encryption_config { + kms_key_name = google_kms_crypto_key.key.id + } + automated_backup_policy { + location = "us-central1" + backup_window = "1800s" + enabled = true + encryption_config { + kms_key_name = google_kms_crypto_key.key2.id + } + time_based_retention { + retention_period = "510s" + } + } + lifecycle { + prevent_destroy = true + } + depends_on = [google_kms_crypto_key_iam_binding.crypto_key] +} + +resource "google_compute_network" "default" { + name = "tf-test-alloydb-cluster%{random_suffix}" +} + +data "google_project" "project" {} + +resource "google_kms_key_ring" "keyring" { + name = "%{key_name}" + location = "us-central1" +} + +resource "google_kms_crypto_key" "key" { + name = "%{key_name}" + key_ring = google_kms_key_ring.keyring.id +} + +resource "google_kms_crypto_key" "key2" { + name = "%{key_name}-2" + key_ring = google_kms_key_ring.keyring.id +} + +resource "google_kms_crypto_key_iam_binding" "crypto_key" { + crypto_key_id = google_kms_crypto_key.key.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + members = [ + "serviceAccount:service-${data.google_project.project.number}@gcp-sa-alloydb.iam.gserviceaccount.com", + ] +} + +resource "google_kms_crypto_key_iam_binding" "crypto_key2" { + crypto_key_id = google_kms_crypto_key.key2.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + members = [ + "serviceAccount:service-${data.google_project.project.number}@gcp-sa-alloydb.iam.gserviceaccount.com", + ] +} +`, context) +} + +func testAccAlloydbCluster_usingCMEKallowDeletion(context map[string]interface{}) string { + return Nprintf(` +resource "google_alloydb_cluster" "default" { + cluster_id = "tf-test-alloydb-cluster%{random_suffix}" + location = "us-central1" + network = "projects/${data.google_project.project.number}/global/networks/${google_compute_network.default.name}" + encryption_config { + kms_key_name = google_kms_crypto_key.key.id + } + automated_backup_policy { + location = "us-central1" + backup_window = "1800s" + enabled = true + encryption_config { + kms_key_name = google_kms_crypto_key.key2.id + } + time_based_retention { + retention_period = "510s" + } + } + depends_on = [google_kms_crypto_key_iam_binding.crypto_key] +} + +resource "google_compute_network" "default" { + name = "tf-test-alloydb-cluster%{random_suffix}" +} + +data "google_project" "project" {} + +resource "google_kms_key_ring" "keyring" { + name = "%{key_name}" + location = "us-central1" +} + +resource "google_kms_crypto_key" "key" { + name = "%{key_name}" + key_ring = google_kms_key_ring.keyring.id +} + +resource "google_kms_crypto_key" "key2" { + name = "%{key_name}-2" + key_ring = google_kms_key_ring.keyring.id +} + +resource "google_kms_crypto_key_iam_binding" "crypto_key" { + crypto_key_id = google_kms_crypto_key.key.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + members = [ + "serviceAccount:service-${data.google_project.project.number}@gcp-sa-alloydb.iam.gserviceaccount.com", + ] +} + +resource "google_kms_crypto_key_iam_binding" "crypto_key2" { + crypto_key_id = google_kms_crypto_key.key2.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + members = [ + "serviceAccount:service-${data.google_project.project.number}@gcp-sa-alloydb.iam.gserviceaccount.com", + ] +} +`, context) +} diff --git a/website/docs/r/alloydb_cluster.html.markdown b/website/docs/r/alloydb_cluster.html.markdown index 5d1a80c851..dc9639fca5 100644 --- a/website/docs/r/alloydb_cluster.html.markdown +++ b/website/docs/r/alloydb_cluster.html.markdown @@ -135,6 +135,11 @@ The following arguments are supported: (Optional) User-defined labels for the alloydb cluster. +* `encryption_config` - + (Optional) + EncryptionConfig describes the encryption config of a cluster or a backup that is encrypted with a CMEK (customer-managed encryption key). + Structure is [documented below](#nested_encryption_config). + * `display_name` - (Optional) User-settable and human-readable display name for the Cluster. @@ -154,6 +159,12 @@ The following arguments are supported: If it is not provided, the provider project is used. +The `encryption_config` block supports: + +* `kms_key_name` - + (Optional) + The fully-qualified resource name of the KMS key. Each Cloud KMS key is regionalized and has the following format: projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME]. + The `initial_user` block supports: * `user` - @@ -181,6 +192,11 @@ The following arguments are supported: (Optional) Labels to apply to backups created using this configuration. +* `encryption_config` - + (Optional) + EncryptionConfig describes the encryption config of a cluster or a backup that is encrypted with a CMEK (customer-managed encryption key). + Structure is [documented below](#nested_encryption_config). + * `weekly_schedule` - (Optional) Weekly schedule for the Backup. @@ -201,6 +217,12 @@ The following arguments are supported: Whether automated backups are enabled. +The `encryption_config` block supports: + +* `kms_key_name` - + (Optional) + The fully-qualified resource name of the KMS key. Each Cloud KMS key is regionalized and has the following format: projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME]. + The `weekly_schedule` block supports: * `days_of_week` - @@ -257,6 +279,10 @@ In addition to the arguments listed above, the following computed attributes are * `uid` - The system-generated UID of the resource. +* `encryption_info` - + EncryptionInfo describes the encryption information of a cluster or a backup. + Structure is [documented below](#nested_encryption_info). + * `database_version` - The database engine major version. This is an output-only field and it's populated at the Cluster creation time. This field cannot be changed after cluster creation. @@ -269,6 +295,16 @@ In addition to the arguments listed above, the following computed attributes are Structure is [documented below](#nested_migration_source). +The `encryption_info` block contains: + +* `encryption_type` - + (Output) + Output only. Type of encryption. + +* `kms_key_versions` - + (Output) + Output only. Cloud KMS key versions that are being used to protect the database or the backup. + The `backup_source` block contains: * `backup_name` -