From d2dd5c7434f24cf856acc82f0e0c11be365d5cb2 Mon Sep 17 00:00:00 2001 From: Roberto Jung Drebes Date: Sat, 29 Jun 2019 21:46:51 +0000 Subject: [PATCH] Pub/Sub Topic CMEK/KMS support Signed-off-by: Modular Magician --- google/resource_pubsub_topic.go | 22 ++++++++++++ google/resource_pubsub_topic_test.go | 43 +++++++++++++++++++++++ website/docs/r/pubsub_topic.html.markdown | 27 ++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/google/resource_pubsub_topic.go b/google/resource_pubsub_topic.go index ac9e00b2d10..6aea6edd553 100644 --- a/google/resource_pubsub_topic.go +++ b/google/resource_pubsub_topic.go @@ -48,6 +48,11 @@ func resourcePubsubTopic() *schema.Resource { ForceNew: true, DiffSuppressFunc: compareSelfLinkOrResourceName, }, + "kms_key_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, "labels": { Type: schema.TypeMap, Optional: true, @@ -73,6 +78,12 @@ func resourcePubsubTopicCreate(d *schema.ResourceData, meta interface{}) error { } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { obj["name"] = nameProp } + kmsKeyNameProp, err := expandPubsubTopicKmsKeyName(d.Get("kms_key_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("kms_key_name"); !isEmptyValue(reflect.ValueOf(kmsKeyNameProp)) && (ok || !reflect.DeepEqual(v, kmsKeyNameProp)) { + obj["kmsKeyName"] = kmsKeyNameProp + } labelsProp, err := expandPubsubTopicLabels(d.Get("labels"), d, config) if err != nil { return err @@ -132,6 +143,9 @@ func resourcePubsubTopicRead(d *schema.ResourceData, meta interface{}) error { if err := d.Set("name", flattenPubsubTopicName(res["name"], d)); err != nil { return fmt.Errorf("Error reading Topic: %s", err) } + if err := d.Set("kms_key_name", flattenPubsubTopicKmsKeyName(res["kmsKeyName"], d)); err != nil { + return fmt.Errorf("Error reading Topic: %s", err) + } if err := d.Set("labels", flattenPubsubTopicLabels(res["labels"], d)); err != nil { return fmt.Errorf("Error reading Topic: %s", err) } @@ -223,6 +237,10 @@ func flattenPubsubTopicName(v interface{}, d *schema.ResourceData) interface{} { return NameFromSelfLinkStateFunc(v) } +func flattenPubsubTopicKmsKeyName(v interface{}, d *schema.ResourceData) interface{} { + return v +} + func flattenPubsubTopicLabels(v interface{}, d *schema.ResourceData) interface{} { return v } @@ -231,6 +249,10 @@ func expandPubsubTopicName(v interface{}, d TerraformResourceData, config *Confi return GetResourceNameFromSelfLink(v.(string)), nil } +func expandPubsubTopicKmsKeyName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandPubsubTopicLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { if v == nil { return map[string]string{}, nil diff --git a/google/resource_pubsub_topic_test.go b/google/resource_pubsub_topic_test.go index 56e6ca83c62..7f1914f72c5 100644 --- a/google/resource_pubsub_topic_test.go +++ b/google/resource_pubsub_topic_test.go @@ -40,6 +40,29 @@ func TestAccPubsubTopic_update(t *testing.T) { }) } +func TestAccPubsubTopic_cmek(t *testing.T) { + t.Parallel() + + kms := BootstrapKMSKey(t) + pid := getTestProjectFromEnv() + topicName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccPubsubTopic_cmek(pid, topicName, kms.CryptoKey.Name), + }, + { + ResourceName: "google_pubsub_topic.topic", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccPubsubTopic_update(topic, key, value string) string { return fmt.Sprintf(` resource "google_pubsub_topic" "foo" { @@ -50,3 +73,23 @@ resource "google_pubsub_topic" "foo" { } `, topic, key, value) } + +func testAccPubsubTopic_cmek(pid, topicName, kmsKey string) 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-pubsub.iam.gserviceaccount.com" +} + +resource "google_pubsub_topic" "topic" { + name = "%s" + project = "${google_project_iam_member.kms-project-binding.project}" + kms_key_name = "%s" +} +`, pid, topicName, kmsKey) +} diff --git a/website/docs/r/pubsub_topic.html.markdown b/website/docs/r/pubsub_topic.html.markdown index d9c5e1892bb..7e8582b7d9d 100644 --- a/website/docs/r/pubsub_topic.html.markdown +++ b/website/docs/r/pubsub_topic.html.markdown @@ -47,6 +47,25 @@ resource "google_pubsub_topic" "example" { } } ``` +## Example Usage - Pubsub Topic Cmek + + +```hcl +resource "google_pubsub_topic" "example" { + name = "example-topic" + kms_key_name = "${google_kms_crypto_key.crypto_key.self_link}" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "example-key" + key_ring = "${google_kms_key_ring.key_ring.self_link}" +} + +resource "google_kms_key_ring" "key_ring" { + name = "example-keyring" + location = "global" +} +``` ## Argument Reference @@ -61,6 +80,14 @@ The following arguments are supported: - - - +* `kms_key_name` - + (Optional) + The resource name of the Cloud KMS CryptoKey to be used to protect access + to messsages published on this topic. Your project's PubSub service account + (`service-{{PROJECT_NUMBER}}@gcp-sa-pubsub.iam.gserviceaccount.com`) must have + `roles/cloudkms.cryptoKeyEncrypterDecrypter` to use this feature. + The expected format is `projects/*/locations/*/keyRings/*/cryptoKeys/*` + * `labels` - (Optional) A set of key/value label pairs to assign to this Topic.