Skip to content

Commit 4931944

Browse files
Support cmek-settings in google_logging_project_bucket_config (#6760) (#13078)
Signed-off-by: Modular Magician <[email protected]> Signed-off-by: Modular Magician <[email protected]>
1 parent 6a1d0a4 commit 4931944

8 files changed

+507
-0
lines changed

.changelog/6760.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```release-note:new-datasource
2+
`google_logging_project_cmek_settings`
3+
```
4+
```release-note:enhancement
5+
logging: added `cmek_settings` field to `google_logging_project_bucket_config` resource
6+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
7+
)
8+
9+
func dataSourceGoogleLoggingProjectCmekSettings() *schema.Resource {
10+
return &schema.Resource{
11+
Read: dataSourceGoogleLoggingProjectCmekSettingsRead,
12+
Schema: map[string]*schema.Schema{
13+
"kms_key_name": {
14+
Type: schema.TypeString,
15+
Optional: true,
16+
Description: `The resource name for the configured Cloud KMS key.
17+
KMS key name format:
18+
"projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEYRING]/cryptoKeys/[KEY]"
19+
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.
20+
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.
21+
See [Enabling CMEK for Logging Buckets](https://cloud.google.com/logging/docs/routing/managed-encryption-storage) for more information.`,
22+
},
23+
"kms_key_version_name": {
24+
Type: schema.TypeString,
25+
Computed: true,
26+
Description: `The CryptoKeyVersion resource name for the configured Cloud KMS key.
27+
KMS key name format:
28+
"projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEYRING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION]"
29+
For example:
30+
"projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1"
31+
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.`,
32+
},
33+
"name": {
34+
Type: schema.TypeString,
35+
Computed: true,
36+
Description: `The resource name of the CMEK settings.`,
37+
},
38+
"service_account_id": {
39+
Type: schema.TypeString,
40+
Computed: true,
41+
Description: `The service account associated with a project for which CMEK will apply.
42+
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.
43+
See [Enabling CMEK for Logging Buckets](https://cloud.google.com/logging/docs/routing/managed-encryption-storage) for more information.`,
44+
},
45+
"project": {
46+
Type: schema.TypeString,
47+
Required: true,
48+
},
49+
},
50+
}
51+
}
52+
53+
func dataSourceGoogleLoggingProjectCmekSettingsRead(d *schema.ResourceData, meta interface{}) error {
54+
config := meta.(*Config)
55+
userAgent, err := generateUserAgentString(d, config.userAgent)
56+
if err != nil {
57+
return err
58+
}
59+
60+
url, err := replaceVars(d, config, "{{LoggingBasePath}}projects/{{project}}/cmekSettings")
61+
if err != nil {
62+
return err
63+
}
64+
65+
billingProject := ""
66+
67+
project, err := getProject(d, config)
68+
if err != nil {
69+
return fmt.Errorf("Error fetching project for ProjectCmekSettings: %s", err)
70+
}
71+
billingProject = project
72+
73+
// err == nil indicates that the billing_project value was found
74+
if bp, err := getBillingProject(d, config); err == nil {
75+
billingProject = bp
76+
}
77+
78+
res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil)
79+
if err != nil {
80+
return handleNotFoundError(err, d, fmt.Sprintf("LoggingProjectCmekSettings %q", d.Id()))
81+
}
82+
83+
d.SetId(fmt.Sprintf("projects/%s/cmekSettings", project))
84+
85+
if err := d.Set("project", project); err != nil {
86+
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
87+
}
88+
89+
if err := d.Set("name", res["name"]); err != nil {
90+
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
91+
}
92+
if err := d.Set("kms_key_name", res["kmsKeyName"]); err != nil {
93+
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
94+
}
95+
if err := d.Set("kms_key_version_name", res["kmsKeyVersionName"]); err != nil {
96+
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
97+
}
98+
if err := d.Set("service_account_id", res["serviceAccountId"]); err != nil {
99+
return fmt.Errorf("Error reading ProjectCmekSettings: %s", err)
100+
}
101+
102+
return nil
103+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8+
)
9+
10+
func TestAccLoggingProjectCmekSettings_basic(t *testing.T) {
11+
t.Parallel()
12+
13+
context := map[string]interface{}{
14+
"project_name": "tf-test-" + randString(t, 10),
15+
"org_id": getTestOrgFromEnv(t),
16+
"billing_account": getTestBillingAccountFromEnv(t),
17+
}
18+
resourceName := "data.google_logging_project_cmek_settings.cmek_settings"
19+
20+
vcrTest(t, resource.TestCase{
21+
PreCheck: func() { testAccPreCheck(t) },
22+
Providers: testAccProviders,
23+
Steps: []resource.TestStep{
24+
{
25+
Config: testAccLoggingProjectCmekSettings_basic(context),
26+
Check: resource.ComposeTestCheckFunc(
27+
resource.TestCheckResourceAttr(
28+
resourceName, "id", fmt.Sprintf("projects/%s/cmekSettings", context["project_name"])),
29+
resource.TestCheckResourceAttr(
30+
resourceName, "name", fmt.Sprintf("projects/%s/cmekSettings", context["project_name"])),
31+
resource.TestCheckResourceAttrSet(resourceName, "service_account_id"),
32+
),
33+
},
34+
},
35+
})
36+
}
37+
38+
func testAccLoggingProjectCmekSettings_basic(context map[string]interface{}) string {
39+
return Nprintf(`
40+
resource "google_project" "default" {
41+
project_id = "%{project_name}"
42+
name = "%{project_name}"
43+
org_id = "%{org_id}"
44+
billing_account = "%{billing_account}"
45+
}
46+
47+
data "google_logging_project_cmek_settings" "cmek_settings" {
48+
project = google_project.default.name
49+
}
50+
`, context)
51+
}

google/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,7 @@ func Provider() *schema.Provider {
874874
"google_folder": dataSourceGoogleFolder(),
875875
"google_folders": dataSourceGoogleFolders(),
876876
"google_folder_organization_policy": dataSourceGoogleFolderOrganizationPolicy(),
877+
"google_logging_project_cmek_settings": dataSourceGoogleLoggingProjectCmekSettings(),
877878
"google_monitoring_notification_channel": dataSourceMonitoringNotificationChannel(),
878879
"google_monitoring_cluster_istio_service": dataSourceMonitoringServiceClusterIstio(),
879880
"google_monitoring_istio_canonical_service": dataSourceMonitoringIstioCanonicalService(),

google/resource_logging_bucket_config.go

+89
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,48 @@ var loggingBucketConfigSchema = map[string]*schema.Schema{
4444
Computed: true,
4545
Description: `The bucket's lifecycle such as active or deleted.`,
4646
},
47+
"cmek_settings": {
48+
Type: schema.TypeList,
49+
MaxItems: 1,
50+
Optional: true,
51+
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.`,
52+
Elem: &schema.Resource{
53+
Schema: map[string]*schema.Schema{
54+
"name": {
55+
Type: schema.TypeString,
56+
Computed: true,
57+
Description: `The resource name of the CMEK settings.`,
58+
},
59+
"kms_key_name": {
60+
Type: schema.TypeString,
61+
Required: true,
62+
Description: `The resource name for the configured Cloud KMS key.
63+
KMS key name format:
64+
"projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEYRING]/cryptoKeys/[KEY]"
65+
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.
66+
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.
67+
See [Enabling CMEK for Logging Buckets](https://cloud.google.com/logging/docs/routing/managed-encryption-storage) for more information.`,
68+
},
69+
"kms_key_version_name": {
70+
Type: schema.TypeString,
71+
Computed: true,
72+
Description: `The CryptoKeyVersion resource name for the configured Cloud KMS key.
73+
KMS key name format:
74+
"projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEYRING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION]"
75+
For example:
76+
"projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1"
77+
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.`,
78+
},
79+
"service_account_id": {
80+
Type: schema.TypeString,
81+
Computed: true,
82+
Description: `The service account associated with a project for which CMEK will apply.
83+
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.
84+
See [Enabling CMEK for Logging Buckets](https://cloud.google.com/logging/docs/routing/managed-encryption-storage) for more information.`,
85+
},
86+
},
87+
},
88+
},
4789
}
4890

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

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

267+
if err := d.Set("cmek_settings", flattenCmekSettings(res["cmekSettings"])); err != nil {
268+
return fmt.Errorf("Error setting cmek_settings: %s", err)
269+
}
270+
224271
return nil
225272
}
226273

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

241288
obj["retentionDays"] = d.Get("retention_days")
242289
obj["description"] = d.Get("description")
290+
obj["cmekSettings"] = expandCmekSettings(d.Get("cmek_settings"))
243291

244292
updateMask := []string{}
245293
if d.HasChange("retention_days") {
@@ -248,6 +296,9 @@ func resourceLoggingBucketConfigUpdate(d *schema.ResourceData, meta interface{})
248296
if d.HasChange("description") {
249297
updateMask = append(updateMask, "description")
250298
}
299+
if d.HasChange("cmek_settings") {
300+
updateMask = append(updateMask, "cmekSettings")
301+
}
251302
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
252303
if err != nil {
253304
return err
@@ -284,3 +335,41 @@ func resourceLoggingBucketConfigDelete(d *schema.ResourceData, meta interface{})
284335
}
285336
return nil
286337
}
338+
339+
func expandCmekSettings(v interface{}) interface{} {
340+
if v == nil {
341+
return nil
342+
}
343+
344+
l := v.([]interface{})
345+
if len(l) == 0 || l[0] == nil {
346+
return nil
347+
}
348+
349+
original := l[0].(map[string]interface{})
350+
351+
transformed := map[string]interface{}{
352+
"name": original["name"],
353+
"kmsKeyName": original["kms_key_name"],
354+
"kmsKeyVersionName": original["kms_key_version_name"],
355+
"serviceAccountId": original["service_account_id"],
356+
}
357+
return transformed
358+
}
359+
360+
func flattenCmekSettings(cmekSettings interface{}) []map[string]interface{} {
361+
if cmekSettings == nil {
362+
return nil
363+
}
364+
365+
cmekSettingsData := cmekSettings.(map[string]interface{})
366+
367+
data := map[string]interface{}{
368+
"name": cmekSettingsData["name"],
369+
"kms_key_name": cmekSettingsData["kmsKeyName"],
370+
"kms_key_version_name": cmekSettingsData["kmsKeyVersionName"],
371+
"service_account_id": cmekSettingsData["serviceAccountId"],
372+
}
373+
374+
return []map[string]interface{}{data}
375+
}

0 commit comments

Comments
 (0)