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

Support cmek-settings in google_logging_project_bucket_config #13078

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/6760.txt
@@ -0,0 +1,6 @@
```release-note:new-datasource
`google_logging_project_cmek_settings`
```
```release-note:enhancement
logging: added `cmek_settings` field to `google_logging_project_bucket_config` resource
```
103 changes: 103 additions & 0 deletions google/data_source_google_logging_project_cmek_settings.go
@@ -0,0 +1,103 @@
package google

import (
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceGoogleLoggingProjectCmekSettings() *schema.Resource {
return &schema.Resource{
Read: dataSourceGoogleLoggingProjectCmekSettingsRead,
Schema: map[string]*schema.Schema{
"kms_key_name": {
Type: schema.TypeString,
Optional: true,
Description: `The resource name for the configured Cloud KMS key.
KMS key name format:
"projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEYRING]/cryptoKeys/[KEY]"
To enable CMEK for the bucket, set this field to a valid kmsKeyName for which the associated service account has the required cloudkms.cryptoKeyEncrypterDecrypter roles assigned for the key.
The Cloud KMS key used by the bucket can be updated by changing the kmsKeyName to a new valid key name. Encryption operations that are in progress will be completed with the key that was in use when they started. Decryption operations will be completed using the key that was used at the time of encryption unless access to that key has been revoked.
See [Enabling CMEK for Logging Buckets](https://cloud.google.com/logging/docs/routing/managed-encryption-storage) for more information.`,
},
"kms_key_version_name": {
Type: schema.TypeString,
Computed: true,
Description: `The CryptoKeyVersion resource name for the configured Cloud KMS key.
KMS key name format:
"projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEYRING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION]"
For example:
"projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1"
This is a read-only field used to convey the specific configured CryptoKeyVersion of kms_key that has been configured. It will be populated in cases where the CMEK settings are bound to a single key version.`,
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: `The resource name of the CMEK settings.`,
},
"service_account_id": {
Type: schema.TypeString,
Computed: true,
Description: `The service account associated with a project for which CMEK will apply.
Before enabling CMEK for a logging bucket, you must first assign the cloudkms.cryptoKeyEncrypterDecrypter role to the service account associated with the project for which CMEK will apply. Use [v2.getCmekSettings](https://cloud.google.com/logging/docs/reference/v2/rest/v2/TopLevel/getCmekSettings#google.logging.v2.ConfigServiceV2.GetCmekSettings) to obtain the service account ID.
See [Enabling CMEK for Logging Buckets](https://cloud.google.com/logging/docs/routing/managed-encryption-storage) for more information.`,
},
"project": {
Type: schema.TypeString,
Required: true,
},
},
}
}

func dataSourceGoogleLoggingProjectCmekSettingsRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
userAgent, err := generateUserAgentString(d, config.userAgent)
if err != nil {
return err
}

url, err := replaceVars(d, config, "{{LoggingBasePath}}projects/{{project}}/cmekSettings")
if err != nil {
return err
}

billingProject := ""

project, err := getProject(d, config)
if err != nil {
return fmt.Errorf("Error fetching project for ProjectCmekSettings: %s", err)
}
billingProject = project

// err == nil indicates that the billing_project value was found
if bp, err := getBillingProject(d, config); err == nil {
billingProject = bp
}

res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil)
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("LoggingProjectCmekSettings %q", d.Id()))
}

d.SetId(fmt.Sprintf("projects/%s/cmekSettings", project))

if err := d.Set("project", project); err != nil {
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
}

if err := d.Set("name", res["name"]); err != nil {
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
}
if err := d.Set("kms_key_name", res["kmsKeyName"]); err != nil {
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
}
if err := d.Set("kms_key_version_name", res["kmsKeyVersionName"]); err != nil {
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
}
if err := d.Set("service_account_id", res["serviceAccountId"]); err != nil {
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
}

return nil
}
51 changes: 51 additions & 0 deletions google/data_source_google_logging_project_cmek_settings_test.go
@@ -0,0 +1,51 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccLoggingProjectCmekSettings_basic(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"project_name": "tf-test-" + randString(t, 10),
"org_id": getTestOrgFromEnv(t),
"billing_account": getTestBillingAccountFromEnv(t),
}
resourceName := "data.google_logging_project_cmek_settings.cmek_settings"

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccLoggingProjectCmekSettings_basic(context),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
resourceName, "id", fmt.Sprintf("projects/%s/cmekSettings", context["project_name"])),
resource.TestCheckResourceAttr(
resourceName, "name", fmt.Sprintf("projects/%s/cmekSettings", context["project_name"])),
resource.TestCheckResourceAttrSet(resourceName, "service_account_id"),
),
},
},
})
}

func testAccLoggingProjectCmekSettings_basic(context map[string]interface{}) string {
return Nprintf(`
resource "google_project" "default" {
project_id = "%{project_name}"
name = "%{project_name}"
org_id = "%{org_id}"
billing_account = "%{billing_account}"
}

data "google_logging_project_cmek_settings" "cmek_settings" {
project = google_project.default.name
}
`, context)
}
1 change: 1 addition & 0 deletions google/provider.go
Expand Up @@ -874,6 +874,7 @@ func Provider() *schema.Provider {
"google_folder": dataSourceGoogleFolder(),
"google_folders": dataSourceGoogleFolders(),
"google_folder_organization_policy": dataSourceGoogleFolderOrganizationPolicy(),
"google_logging_project_cmek_settings": dataSourceGoogleLoggingProjectCmekSettings(),
"google_monitoring_notification_channel": dataSourceMonitoringNotificationChannel(),
"google_monitoring_cluster_istio_service": dataSourceMonitoringServiceClusterIstio(),
"google_monitoring_istio_canonical_service": dataSourceMonitoringIstioCanonicalService(),
Expand Down
89 changes: 89 additions & 0 deletions google/resource_logging_bucket_config.go
Expand Up @@ -44,6 +44,48 @@ var loggingBucketConfigSchema = map[string]*schema.Schema{
Computed: true,
Description: `The bucket's lifecycle such as active or deleted.`,
},
"cmek_settings": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Description: `The CMEK settings of the log bucket. If present, new log entries written to this log bucket are encrypted using the CMEK key provided in this configuration. If a log bucket has CMEK settings, the CMEK settings cannot be disabled later by updating the log bucket. Changing the KMS key is allowed.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
Description: `The resource name of the CMEK settings.`,
},
"kms_key_name": {
Type: schema.TypeString,
Required: true,
Description: `The resource name for the configured Cloud KMS key.
KMS key name format:
"projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEYRING]/cryptoKeys/[KEY]"
To enable CMEK for the bucket, set this field to a valid kmsKeyName for which the associated service account has the required cloudkms.cryptoKeyEncrypterDecrypter roles assigned for the key.
The Cloud KMS key used by the bucket can be updated by changing the kmsKeyName to a new valid key name. Encryption operations that are in progress will be completed with the key that was in use when they started. Decryption operations will be completed using the key that was used at the time of encryption unless access to that key has been revoked.
See [Enabling CMEK for Logging Buckets](https://cloud.google.com/logging/docs/routing/managed-encryption-storage) for more information.`,
},
"kms_key_version_name": {
Type: schema.TypeString,
Computed: true,
Description: `The CryptoKeyVersion resource name for the configured Cloud KMS key.
KMS key name format:
"projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEYRING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION]"
For example:
"projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1"
This is a read-only field used to convey the specific configured CryptoKeyVersion of kms_key that has been configured. It will be populated in cases where the CMEK settings are bound to a single key version.`,
},
"service_account_id": {
Type: schema.TypeString,
Computed: true,
Description: `The service account associated with a project for which CMEK will apply.
Before enabling CMEK for a logging bucket, you must first assign the cloudkms.cryptoKeyEncrypterDecrypter role to the service account associated with the project for which CMEK will apply. Use [v2.getCmekSettings](https://cloud.google.com/logging/docs/reference/v2/rest/v2/TopLevel/getCmekSettings#google.logging.v2.ConfigServiceV2.GetCmekSettings) to obtain the service account ID.
See [Enabling CMEK for Logging Buckets](https://cloud.google.com/logging/docs/routing/managed-encryption-storage) for more information.`,
},
},
},
},
}

type loggingBucketConfigIDFunc func(d *schema.ResourceData, config *Config) (string, error)
Expand Down Expand Up @@ -154,6 +196,7 @@ func resourceLoggingBucketConfigCreate(d *schema.ResourceData, meta interface{},
obj["description"] = d.Get("description")
obj["retentionDays"] = d.Get("retention_days")
obj["locked"] = d.Get("locked")
obj["cmekSettings"] = expandCmekSettings(d.Get("cmek_settings"))

url, err := replaceVars(d, config, "{{LoggingBasePath}}projects/{{project}}/locations/{{location}}/buckets?bucketId={{bucket_id}}")
if err != nil {
Expand Down Expand Up @@ -221,6 +264,10 @@ func resourceLoggingBucketConfigRead(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("Error setting retention_days: %s", err)
}

if err := d.Set("cmek_settings", flattenCmekSettings(res["cmekSettings"])); err != nil {
return fmt.Errorf("Error setting cmek_settings: %s", err)
}

return nil
}

Expand All @@ -240,6 +287,7 @@ func resourceLoggingBucketConfigUpdate(d *schema.ResourceData, meta interface{})

obj["retentionDays"] = d.Get("retention_days")
obj["description"] = d.Get("description")
obj["cmekSettings"] = expandCmekSettings(d.Get("cmek_settings"))

updateMask := []string{}
if d.HasChange("retention_days") {
Expand All @@ -248,6 +296,9 @@ func resourceLoggingBucketConfigUpdate(d *schema.ResourceData, meta interface{})
if d.HasChange("description") {
updateMask = append(updateMask, "description")
}
if d.HasChange("cmek_settings") {
updateMask = append(updateMask, "cmekSettings")
}
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
if err != nil {
return err
Expand Down Expand Up @@ -284,3 +335,41 @@ func resourceLoggingBucketConfigDelete(d *schema.ResourceData, meta interface{})
}
return nil
}

func expandCmekSettings(v interface{}) interface{} {
if v == nil {
return nil
}

l := v.([]interface{})
if len(l) == 0 || l[0] == nil {
return nil
}

original := l[0].(map[string]interface{})

transformed := map[string]interface{}{
"name": original["name"],
"kmsKeyName": original["kms_key_name"],
"kmsKeyVersionName": original["kms_key_version_name"],
"serviceAccountId": original["service_account_id"],
}
return transformed
}

func flattenCmekSettings(cmekSettings interface{}) []map[string]interface{} {
if cmekSettings == nil {
return nil
}

cmekSettingsData := cmekSettings.(map[string]interface{})

data := map[string]interface{}{
"name": cmekSettingsData["name"],
"kms_key_name": cmekSettingsData["kmsKeyName"],
"kms_key_version_name": cmekSettingsData["kmsKeyVersionName"],
"service_account_id": cmekSettingsData["serviceAccountId"],
}

return []map[string]interface{}{data}
}