Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Bigtable CMEK Support #4770

Merged
merged 2 commits into from
Jun 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ func resourceBigtableInstance() *schema.Resource {
},
},

// ----------------------------------------------------------------------
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note:
We are moving ahead with removing this now that deletion_protection is available.

// IMPORTANT: Do not add any additional ForceNew fields to this resource.
// Destroying/recreating instances can lead to data loss for users.
// ----------------------------------------------------------------------
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Expand Down Expand Up @@ -82,6 +78,13 @@ func resourceBigtableInstance() *schema.Resource {
ValidateFunc: validation.StringInSlice([]string{"SSD", "HDD"}, false),
Description: `The storage type to use. One of "SSD" or "HDD". Defaults to "SSD".`,
},
"kms_key_name": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for context:
This field is optional+computed and deletion protection is available. Introducing this as a new ForceNew field should have lower risk than before.

Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
Description: `Describes the Cloud KMS encryption key that will be used to protect the destination Bigtable cluster. The requirements for this key are: 1) The Cloud Bigtable service account associated with the project that contains this cluster must be granted the cloudkms.cryptoKeyEncrypterDecrypter role on the CMEK key. 2) Only regional keys can be used and the region of the CMEK key must match the region of the cluster. 3) All clusters within an instance must use the same CMEK key. Values are of the form projects/{project}/locations/{location}/keyRings/{keyring}/cryptoKeys/{key}`,
},
},
},
},
Expand Down Expand Up @@ -354,6 +357,7 @@ func flattenBigtableCluster(c *bigtable.ClusterInfo) map[string]interface{} {
"num_nodes": c.ServeNodes,
"cluster_id": c.Name,
"storage_type": storageType,
"kms_key_name": c.KMSKeyName,
}
}

Expand All @@ -378,6 +382,7 @@ func expandBigtableClusters(clusters []interface{}, instanceID string, config *C
ClusterID: cluster["cluster_id"].(string),
NumNodes: int32(cluster["num_nodes"].(int)),
StorageType: storageType,
KMSKeyName: cluster["kms_key_name"].(string),
})
}
return results, nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,33 @@ func TestAccBigtableInstance_allowDestroy(t *testing.T) {
})
}

func TestAccBigtableInstance_kms(t *testing.T) {
// bigtable instance does not use the shared HTTP client, this test creates an instance
skipIfVcr(t)
t.Parallel()

kms := BootstrapKMSKeyInLocation(t, "us-central1")
pid := getTestProjectFromEnv()
instanceName := fmt.Sprintf("tf-test-%s", randString(t, 10))

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBigtableInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccBigtableInstance_kms(pid, instanceName, kms.CryptoKey.Name, 3),
},
{
ResourceName: "google_bigtable_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"}, // we don't read instance type back
},
},
})
}

func testAccCheckBigtableInstanceDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
var ctx = context.Background()
Expand Down Expand Up @@ -339,3 +366,32 @@ resource "google_bigtable_instance" "instance" {
}
`, instanceName, instanceName, numNodes)
}

func testAccBigtableInstance_kms(pid, instanceName, kmsKey string, numNodes int) string {
return fmt.Sprintf(`
data "google_project" "project" {
project_id = "%s"
}


resource "google_project_iam_member" "kms_project_binding" {
project = data.google_project.project.project_id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-bigtable.iam.gserviceaccount.com"
}

resource "google_bigtable_instance" "instance" {
name = "%s"
cluster {
cluster_id = "%s"
zone = "us-central1-b"
num_nodes = %d
storage_type = "HDD"
kms_key_name = "%s"
}
depends_on = [google_project_iam_member.kms_project_binding]
deletion_protection = false

}
`, pid, instanceName, instanceName, numNodes, kmsKey)
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ for a `DEVELOPMENT` instance.
* `storage_type` - (Optional) The storage type to use. One of `"SSD"` or
`"HDD"`. Defaults to `"SSD"`.

* `kms_key_name` - (Optional) Describes the Cloud KMS encryption key that will be used to protect the destination Bigtable cluster. The requirements for this key are: 1) The Cloud Bigtable service account associated with the project that contains this cluster must be granted the `cloudkms.cryptoKeyEncrypterDecrypter` role on the CMEK key. 2) Only regional keys can be used and the region of the CMEK key must match the region of the cluster. 3) All clusters within an instance must use the same CMEK key. Values are of the form `projects/{project}/locations/{location}/keyRings/{keyring}/cryptoKeys/{key}`
c2thorn marked this conversation as resolved.
Show resolved Hide resolved

!> **Warning**: Modifying this field will cause Terraform to delete/recreate the entire resource.

-> **Note**: To remove this field once it is set, set the value to an empty string. Removing the field entirely from the config will cause the provider to default to the backend value.

!> **Warning:** Modifying the `storage_type` or `zone` of an existing cluster (by
`cluster_id`) will cause Terraform to delete/recreate the entire
`google_bigtable_instance` resource. If these values are changing, use a new
Expand Down