Skip to content

Commit 65b284f

Browse files
Add data source for keyhandle list (#12708) (#9105)
[upstream:e055e09e6a5644b159da84f94759af5ee5b0d047] Signed-off-by: Modular Magician <[email protected]>
1 parent 12d4d71 commit 65b284f

File tree

5 files changed

+301
-0
lines changed

5 files changed

+301
-0
lines changed

.changelog/12708.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:new-datasource
2+
`google_kms_key_handles`
3+
```

google-beta/provider/provider_mmv1_resources.go

+1
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ var handwrittenDatasources = map[string]*schema.Resource{
289289
"google_kms_key_rings": kms.DataSourceGoogleKmsKeyRings(),
290290
"google_kms_key_handle": kms.DataSourceGoogleKmsKeyHandle(),
291291
"google_kms_autokey_config": kms.DataSourceGoogleKmsAutokeyConfig(),
292+
"google_kms_key_handles": kms.DataSourceGoogleKmsKeyHandles(),
292293
"google_kms_secret": kms.DataSourceGoogleKmsSecret(),
293294
"google_kms_secret_ciphertext": kms.DataSourceGoogleKmsSecretCiphertext(),
294295
"google_kms_secret_asymmetric": kms.DataSourceGoogleKmsSecretAsymmetric(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
// Copyright (c) HashiCorp, Inc.
4+
// SPDX-License-Identifier: MPL-2.0
5+
package kms
6+
7+
import (
8+
"fmt"
9+
"log"
10+
"strings"
11+
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13+
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
14+
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
15+
)
16+
17+
func DataSourceGoogleKmsKeyHandles() *schema.Resource {
18+
return &schema.Resource{
19+
Read: dataSourceGoogleKmsKeyHandlesRead,
20+
Schema: map[string]*schema.Schema{
21+
"project": {
22+
Type: schema.TypeString,
23+
Optional: true,
24+
Description: `Project ID of the project.`,
25+
},
26+
"location": {
27+
Type: schema.TypeString,
28+
Required: true,
29+
Description: `The canonical id for the location. For example: "us-east1".`,
30+
},
31+
"resource_type_selector": {
32+
Type: schema.TypeString,
33+
Required: true,
34+
Description: `
35+
The resource_type_selector argument is used to add a filter query parameter that limits which key handles are retrieved by the data source: ?filter=resource_type_selector="{{resource_type_selector}}".
36+
Example values:
37+
* resource_type_selector="{SERVICE}.googleapis.com/{TYPE}".
38+
[See the documentation about using filters](https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyHandles/list)
39+
`,
40+
},
41+
"key_handles": {
42+
Type: schema.TypeList,
43+
Computed: true,
44+
Description: "A list of all the retrieved key handles",
45+
Elem: &schema.Resource{
46+
Schema: map[string]*schema.Schema{
47+
"name": {
48+
Type: schema.TypeString,
49+
Computed: true,
50+
},
51+
"kms_key": {
52+
Type: schema.TypeString,
53+
Computed: true,
54+
},
55+
"resource_type_selector": {
56+
Type: schema.TypeString,
57+
Computed: true,
58+
},
59+
},
60+
},
61+
},
62+
},
63+
}
64+
65+
}
66+
67+
func dataSourceGoogleKmsKeyHandlesRead(d *schema.ResourceData, meta interface{}) error {
68+
config := meta.(*transport_tpg.Config)
69+
project, err := tpgresource.GetProject(d, config)
70+
if err != nil {
71+
return err
72+
}
73+
resourceTypeSelector := ""
74+
if fl, ok := d.GetOk("resource_type_selector"); ok {
75+
resourceTypeSelector = strings.Replace(fl.(string), "\"", "%22", -1)
76+
}
77+
78+
billingProject := project
79+
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
80+
billingProject = bp
81+
}
82+
83+
url, err := tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}projects/{{project}}/locations/{{location}}/keyHandles")
84+
if err != nil {
85+
return err
86+
}
87+
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
88+
params := make(map[string]string)
89+
var keyHandles []interface{}
90+
for {
91+
newUrl, err := addQueryParams(url, resourceTypeSelector, params)
92+
if err != nil {
93+
return err
94+
}
95+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
96+
Config: config,
97+
Method: "GET",
98+
Project: billingProject,
99+
RawURL: newUrl,
100+
UserAgent: userAgent,
101+
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.Is429RetryableQuotaError},
102+
})
103+
if err != nil {
104+
return fmt.Errorf("Error retrieving keyhandles: %s", err)
105+
}
106+
107+
if res["keyHandles"] == nil {
108+
break
109+
}
110+
pageKeyHandles, err := flattenKMSKeyHandlesList(config, res["keyHandles"])
111+
if err != nil {
112+
return fmt.Errorf("error flattening key handle list: %s", err)
113+
}
114+
keyHandles = append(keyHandles, pageKeyHandles...)
115+
116+
pToken, ok := res["nextPageToken"]
117+
if ok && pToken != nil && pToken.(string) != "" {
118+
params["pageToken"] = pToken.(string)
119+
} else {
120+
break
121+
}
122+
}
123+
log.Printf("[DEBUG] Found %d key handles", len(keyHandles))
124+
if err := d.Set("key_handles", keyHandles); err != nil {
125+
return fmt.Errorf("error setting key handles: %s", err)
126+
}
127+
d.SetId(fmt.Sprintf("projects/%s/locations/%s/keyHandles?filter=resource_type_selector=%s", project, d.Get("location"), resourceTypeSelector))
128+
return nil
129+
}
130+
131+
// transport_tpg.AddQueryParams() encodes the filter=resource_type_selector="value" into
132+
// filter=resource_type_selector%3D%22value%22
133+
// The encoding of '=' into %3D is currently causing issue with ListKeyHandle api.
134+
// To to handle this case currently, as part of this function,
135+
// we are manually adding filter as a query param to the url
136+
func addQueryParams(url string, resourceTypeSelector string, params map[string]string) (string, error) {
137+
quoteEncoding := "%22"
138+
if len(params) == 0 {
139+
return fmt.Sprintf("%s?filter=resource_type_selector=%s%s%s", url, quoteEncoding, resourceTypeSelector, quoteEncoding), nil
140+
} else {
141+
url, err := transport_tpg.AddQueryParams(url, params)
142+
if err != nil {
143+
return "", nil
144+
}
145+
return fmt.Sprintf("%s&filter=resource_type_selector=%s%s%s", url, quoteEncoding, resourceTypeSelector, quoteEncoding), nil
146+
}
147+
}
148+
149+
// flattenKMSKeyHandlesList flattens a list of key handles
150+
func flattenKMSKeyHandlesList(config *transport_tpg.Config, keyHandlesList interface{}) ([]interface{}, error) {
151+
var keyHandles []interface{}
152+
for _, k := range keyHandlesList.([]interface{}) {
153+
keyHandle := k.(map[string]interface{})
154+
155+
data := map[string]interface{}{}
156+
data["name"] = keyHandle["name"]
157+
data["kms_key"] = keyHandle["kmsKey"]
158+
data["resource_type_selector"] = keyHandle["resourceTypeSelector"]
159+
160+
keyHandles = append(keyHandles, data)
161+
}
162+
163+
return keyHandles, nil
164+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
// Copyright (c) HashiCorp, Inc.
4+
// SPDX-License-Identifier: MPL-2.0
5+
package kms_test
6+
7+
import (
8+
"errors"
9+
"fmt"
10+
"strconv"
11+
"strings"
12+
"testing"
13+
14+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
15+
"github.com/hashicorp/terraform-plugin-testing/terraform"
16+
"github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest"
17+
)
18+
19+
func TestAccDataSourceGoogleKmsKeyHandles_basic(t *testing.T) {
20+
kmsAutokey := acctest.BootstrapKMSAutokeyKeyHandle(t)
21+
keyParts := strings.Split(kmsAutokey.KeyHandle.Name, "/")
22+
project := keyParts[1]
23+
location := keyParts[3]
24+
diskFilter := fmt.Sprintf("compute.googleapis.com/Disk")
25+
26+
acctest.VcrTest(t, resource.TestCase{
27+
PreCheck: func() { acctest.AccTestPreCheck(t) },
28+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
29+
Steps: []resource.TestStep{
30+
{
31+
Config: testAccDataSourceGoogleKmsKeyHandles_basic(project, location, diskFilter),
32+
Check: resource.ComposeTestCheckFunc(
33+
validateKeyHandleName(
34+
"data.google_kms_key_handles.mykeyhandles", kmsAutokey.KeyHandle.Name,
35+
),
36+
),
37+
},
38+
},
39+
})
40+
}
41+
func validateKeyHandleName(dataSourceName string, expectedKeyHandleName string) func(*terraform.State) error {
42+
return func(s *terraform.State) error {
43+
ds, ok := s.RootModule().Resources[dataSourceName]
44+
if !ok {
45+
return fmt.Errorf("can't find %s in state", dataSourceName)
46+
}
47+
48+
var dsAttr map[string]string
49+
dsAttr = ds.Primary.Attributes
50+
51+
totalKeyHandles, err := strconv.Atoi(dsAttr["key_handles.#"])
52+
if err != nil {
53+
return errors.New("Couldn't convert length of key_handles list to integer")
54+
}
55+
if totalKeyHandles != 1 {
56+
return errors.New(fmt.Sprintf("want 1 keyhandle, found %d", totalKeyHandles))
57+
}
58+
actualKeyHandleName := dsAttr["key_handles.0.name"]
59+
if actualKeyHandleName != expectedKeyHandleName {
60+
return errors.New(fmt.Sprintf("want keyhandle name %s, got: %s", expectedKeyHandleName, actualKeyHandleName))
61+
}
62+
return nil
63+
}
64+
}
65+
func testAccDataSourceGoogleKmsKeyHandles_basic(project string, location string, filter string) string {
66+
str := fmt.Sprintf(`
67+
data "google_kms_key_handles" "mykeyhandles" {
68+
project = "%s"
69+
location = "%s"
70+
resource_type_selector = "%s"
71+
}
72+
`, project, location, filter)
73+
return str
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
subcategory: "Cloud Key Management Service"
3+
description: |-
4+
Provides access to KMS key handle data with Google Cloud KMS.
5+
---
6+
7+
# google_kms_key_handles
8+
9+
Provides access to Google Cloud Platform KMS KeyHandle. A key handle is a Cloud KMS resource that helps you safely span the separation of duties to create new Cloud KMS keys for CMEK using Autokey.
10+
11+
~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider.
12+
See [Provider Versions](https://terraform.io/docs/providers/google/guides/provider_versions.html) for more details on beta resources.
13+
14+
For more information see
15+
[the official documentation](https://cloud.google.com/kms/docs/resource-hierarchy#key_handles)
16+
and
17+
[API](https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyHandles/list).
18+
19+
20+
## Example Usage
21+
22+
```hcl
23+
data "google_kms_key_handles" "my_key_handles" {
24+
project = "resource-project-id"
25+
location = "us-central1"
26+
resource_type_selector = "storage.googleapis.com/Bucket"
27+
}
28+
```
29+
30+
## Argument Reference
31+
32+
The following arguments are supported:
33+
34+
* `location` - (Required) The Google Cloud Platform location for the KeyHandle.
35+
A full list of valid locations can be found by running `gcloud kms locations list`.
36+
37+
* `resource_type_selector` - (Required) The resource type by which to filter KeyHandle e.g. {SERVICE}.googleapis.com/{TYPE}. See documentation for supported resource types.
38+
39+
- - -
40+
41+
* `project` - (Optional) The project in which the resource belongs. If it
42+
is not provided, the provider project is used.
43+
44+
## Attributes Reference
45+
46+
In addition to the arguments listed above, the following computed attributes are
47+
exported:
48+
49+
* `name` - The name of the KeyHandle. Its format is `projects/{projectId}/locations/{location}/keyHandles/{keyHandleName}`.
50+
51+
* `kms_key` - The identifier of the KMS Key created for the KeyHandle. Its format is `projects/{projectId}/locations/{location}/keyRings/{keyRingName}/cryptoKeys/{cryptoKeyName}`.
52+
53+
* `location` - The location of the KMS Key and KeyHandle.
54+
55+
* `project` - The identifier of the project where KMS KeyHandle is created.
56+
57+
* `resource_type_selector` - Indicates the resource type that the resulting CryptoKey is meant to protect, e.g. {SERVICE}.googleapis.com/{TYPE}. See documentation for supported resource types.
58+
59+

0 commit comments

Comments
 (0)