Skip to content

Commit cf7fa9d

Browse files
authored
KMS: add google_kms_crypto_key_latest_version data source (#11456)
1 parent 4c3e8a6 commit cf7fa9d

6 files changed

+330
-3
lines changed

mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ var handwrittenDatasources = map[string]*schema.Resource{
139139
"google_kms_crypto_key": kms.DataSourceGoogleKmsCryptoKey(),
140140
"google_kms_crypto_keys": kms.DataSourceGoogleKmsCryptoKeys(),
141141
"google_kms_crypto_key_version": kms.DataSourceGoogleKmsCryptoKeyVersion(),
142+
"google_kms_crypto_key_latest_version": kms.DataSourceGoogleKmsLatestCryptoKeyVersion(),
142143
"google_kms_crypto_key_versions": kms.DataSourceGoogleKmsCryptoKeyVersions(),
143144
"google_kms_key_ring": kms.DataSourceGoogleKmsKeyRing(),
144145
"google_kms_key_rings": kms.DataSourceGoogleKmsKeyRings(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package kms
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
8+
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
)
12+
13+
func DataSourceGoogleKmsLatestCryptoKeyVersion() *schema.Resource {
14+
return &schema.Resource{
15+
Read: dataSourceGoogleKmsLatestCryptoKeyVersionRead,
16+
Schema: map[string]*schema.Schema{
17+
"crypto_key": {
18+
Type: schema.TypeString,
19+
Required: true,
20+
ForceNew: true,
21+
},
22+
"name": {
23+
Type: schema.TypeString,
24+
Computed: true,
25+
},
26+
"version": {
27+
Type: schema.TypeInt,
28+
Computed: true,
29+
},
30+
"algorithm": {
31+
Type: schema.TypeString,
32+
Computed: true,
33+
},
34+
"protection_level": {
35+
Type: schema.TypeString,
36+
Computed: true,
37+
},
38+
"state": {
39+
Type: schema.TypeString,
40+
Computed: true,
41+
},
42+
"filter": {
43+
Type: schema.TypeString,
44+
Optional: true,
45+
Description: `
46+
The filter argument is used to add a filter query parameter that limits which type of cryptoKeyVersion is retrieved as the latest by the data source: ?filter={{filter}}. When no value is provided there is no filtering.
47+
48+
Example filter values if filtering on state.
49+
50+
* "state:ENABLED" will retrieve the latest cryptoKeyVersion that has the state "ENABLED".
51+
52+
[See the documentation about using filters](https://cloud.google.com/kms/docs/sorting-and-filtering)
53+
`,
54+
},
55+
"public_key": {
56+
Type: schema.TypeList,
57+
Computed: true,
58+
Elem: &schema.Resource{
59+
Schema: map[string]*schema.Schema{
60+
"algorithm": {
61+
Type: schema.TypeString,
62+
Computed: true,
63+
},
64+
"pem": {
65+
Type: schema.TypeString,
66+
Computed: true,
67+
},
68+
},
69+
},
70+
},
71+
},
72+
}
73+
}
74+
75+
func dataSourceGoogleKmsLatestCryptoKeyVersionRead(d *schema.ResourceData, meta interface{}) error {
76+
config := meta.(*transport_tpg.Config)
77+
78+
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
79+
if err != nil {
80+
return err
81+
}
82+
83+
cryptoKeyId, err := ParseKmsCryptoKeyId(d.Get("crypto_key").(string), config)
84+
if err != nil {
85+
return err
86+
}
87+
88+
id := fmt.Sprintf("%s/latestCryptoKeyVersion", cryptoKeyId.CryptoKeyId())
89+
if filter, ok := d.GetOk("filter"); ok {
90+
id += "/filter=" + filter.(string)
91+
}
92+
d.SetId(id)
93+
94+
versions, err := dataSourceKMSCryptoKeyVersionsList(d, meta, cryptoKeyId.CryptoKeyId(), userAgent)
95+
if err != nil {
96+
return err
97+
}
98+
99+
// grab latest version
100+
lv := len(versions) - 1
101+
if lv < 0 {
102+
return fmt.Errorf("No CryptoVersions found in crypto key %s", cryptoKeyId.CryptoKeyId())
103+
}
104+
105+
latestVersion := versions[lv].(map[string]interface{})
106+
107+
// The google_kms_crypto_key resource and dataset set
108+
// id as the value of name (projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}/cryptoKeys/{{name}})
109+
// and set name is set as just {{name}}.
110+
111+
if err := d.Set("name", flattenKmsCryptoKeyVersionName(latestVersion["name"], d)); err != nil {
112+
return fmt.Errorf("Error setting LatestCryptoKeyVersion: %s", err)
113+
}
114+
if err := d.Set("version", flattenKmsCryptoKeyVersionVersion(latestVersion["name"], d)); err != nil {
115+
return fmt.Errorf("Error setting CryptoKeyVersion: %s", err)
116+
}
117+
if err := d.Set("state", flattenKmsCryptoKeyVersionState(latestVersion["state"], d)); err != nil {
118+
return fmt.Errorf("Error setting LatestCryptoKeyVersion: %s", err)
119+
}
120+
if err := d.Set("protection_level", flattenKmsCryptoKeyVersionProtectionLevel(latestVersion["protectionLevel"], d)); err != nil {
121+
return fmt.Errorf("Error setting LatestCryptoKeyVersion: %s", err)
122+
}
123+
if err := d.Set("algorithm", flattenKmsCryptoKeyVersionAlgorithm(latestVersion["algorithm"], d)); err != nil {
124+
return fmt.Errorf("Error setting LatestCryptoKeyVersion: %s", err)
125+
}
126+
127+
url, err := tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}/cryptoKeyVersions/{{version}}")
128+
if err != nil {
129+
return err
130+
}
131+
132+
log.Printf("[DEBUG] Getting attributes for CryptoKeyVersion: %#v", url)
133+
134+
url, err = tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}")
135+
if err != nil {
136+
return err
137+
}
138+
139+
log.Printf("[DEBUG] Getting purpose of CryptoKey: %#v", url)
140+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
141+
Config: config,
142+
Method: "GET",
143+
Project: cryptoKeyId.KeyRingId.Project,
144+
RawURL: url,
145+
UserAgent: userAgent,
146+
})
147+
if err != nil {
148+
return transport_tpg.HandleDataSourceNotFoundError(err, d, fmt.Sprintf("KmsCryptoKey %q", d.Id()), url)
149+
}
150+
151+
if res["purpose"] == "ASYMMETRIC_SIGN" || res["purpose"] == "ASYMMETRIC_DECRYPT" {
152+
url, err = tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}/cryptoKeyVersions/{{version}}/publicKey")
153+
if err != nil {
154+
return err
155+
}
156+
log.Printf("[DEBUG] Getting public key of CryptoKeyVersion: %#v", url)
157+
158+
res, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
159+
Config: config,
160+
Method: "GET",
161+
Project: cryptoKeyId.KeyRingId.Project,
162+
RawURL: url,
163+
UserAgent: userAgent,
164+
Timeout: d.Timeout(schema.TimeoutRead),
165+
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.IsCryptoKeyVersionsPendingGeneration},
166+
})
167+
168+
if err != nil {
169+
log.Printf("Error generating public key: %s", err)
170+
return err
171+
}
172+
173+
if err := d.Set("public_key", flattenKmsCryptoKeyVersionPublicKey(res, d)); err != nil {
174+
return fmt.Errorf("Error setting CryptoKeyVersion public key: %s", err)
175+
}
176+
}
177+
178+
return nil
179+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
package kms_test
4+
5+
import (
6+
"fmt"
7+
"regexp"
8+
"testing"
9+
10+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
11+
"github.com/hashicorp/terraform-provider-google/google/acctest"
12+
)
13+
14+
func TestAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(t *testing.T) {
15+
asymSignKey := acctest.BootstrapKMSKeyWithPurpose(t, "ASYMMETRIC_SIGN")
16+
17+
id := asymSignKey.CryptoKey.Name + "/latestCryptoKeyVersion"
18+
19+
randomString := acctest.RandString(t, 10)
20+
filterNameFindsNoLatestCryptoKeyVersion := fmt.Sprintf("name:%s", randomString)
21+
filterNameFindEnabledLatestCryptoKeyVersion := "state:enabled"
22+
23+
findsNoLatestCryptoKeyVersionId := fmt.Sprintf("%s/filter=%s", id, filterNameFindsNoLatestCryptoKeyVersion)
24+
findsEnabledLatestCryptoKeyVersionId := fmt.Sprintf("%s/filter=%s", id, filterNameFindEnabledLatestCryptoKeyVersion)
25+
26+
context := map[string]interface{}{
27+
"crypto_key": asymSignKey.CryptoKey.Name,
28+
"filter": "", // Can be overridden using 2nd argument to config funcs
29+
}
30+
31+
acctest.VcrTest(t, resource.TestCase{
32+
PreCheck: func() { acctest.AccTestPreCheck(t) },
33+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
34+
Steps: []resource.TestStep{
35+
{
36+
Config: testAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(context, ""),
37+
// Test will attempt to get the latest version from the list of cryptoKeyVersions, if the latest is not enabled it will return an error
38+
Check: resource.ComposeTestCheckFunc(
39+
resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "id", id),
40+
resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "crypto_key", asymSignKey.CryptoKey.Name),
41+
resource.TestMatchResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "version", regexp.MustCompile("[1-9]+[0-9]*")),
42+
),
43+
ExpectError: regexp.MustCompile("Error: googleapi: Error 400:"),
44+
},
45+
{
46+
Config: testAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(context, fmt.Sprintf("filter = \"%s\"", filterNameFindEnabledLatestCryptoKeyVersion)),
47+
Check: resource.ComposeTestCheckFunc(
48+
// This filter should retrieve the latest ENABLED cryptoKeyVersion in the bootstrapped KMS crypto key used by the test
49+
resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "id", findsEnabledLatestCryptoKeyVersionId),
50+
resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "crypto_key", asymSignKey.CryptoKey.Name),
51+
resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "state", "ENABLED"),
52+
),
53+
},
54+
{
55+
Config: testAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(context, fmt.Sprintf("filter = \"%s\"", filterNameFindsNoLatestCryptoKeyVersion)),
56+
Check: resource.ComposeTestCheckFunc(
57+
// This filter should retrieve no latest version
58+
resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "id", findsNoLatestCryptoKeyVersionId),
59+
resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "crypto_key", asymSignKey.CryptoKey.Name),
60+
),
61+
ExpectError: regexp.MustCompile("Error: No CryptoVersions found in crypto key"),
62+
},
63+
},
64+
})
65+
}
66+
67+
func testAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(context map[string]interface{}, filter string) string {
68+
context["filter"] = filter
69+
70+
return acctest.Nprintf(`
71+
data "google_kms_crypto_key_latest_version" "latest_version" {
72+
crypto_key = "%{crypto_key}"
73+
%{filter}
74+
}
75+
`, context)
76+
}

mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_versions.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// Copyright (c) HashiCorp, Inc.
22
// SPDX-License-Identifier: MPL-2.0
3-
// Copyright (c) HashiCorp, Inc.
4-
// SPDX-License-Identifier: MPL-2.0
53
package kms
64

75
import (
@@ -161,7 +159,7 @@ func dataSourceGoogleKmsCryptoKeyVersionsRead(d *schema.ResourceData, meta inter
161159
func dataSourceKMSCryptoKeyVersionsList(d *schema.ResourceData, meta interface{}, cryptoKeyId string, userAgent string) ([]interface{}, error) {
162160
config := meta.(*transport_tpg.Config)
163161

164-
url, err := tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}/cryptoKeyVersions?filter=state=ENABLED")
162+
url, err := tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}/cryptoKeyVersions")
165163
if err != nil {
166164
return nil, err
167165
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
subcategory: "Cloud Key Management Service"
3+
description: |-
4+
Provides access to the latest KMS key version data with Google Cloud KMS.
5+
---
6+
7+
# google_kms_crypto_key_latest_version
8+
9+
Provides access to the latest Google Cloud Platform KMS CryptoKeyVersion in a CryptoKey. For more information see
10+
[the official documentation](https://cloud.google.com/kms/docs/object-hierarchy#key_version)
11+
and
12+
[API](https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys.cryptoKeyVersions).
13+
14+
## Example Usage
15+
16+
```hcl
17+
data "google_kms_key_ring" "my_key_ring" {
18+
name = "my-key-ring"
19+
location = "us-central1"
20+
}
21+
22+
data "google_kms_crypto_key" "my_crypto_key" {
23+
name = "my-crypto-key"
24+
key_ring = data.google_kms_key_ring.my_key_ring.id
25+
}
26+
27+
data "google_kms_crypto_key_latest_version" "my_crypto_key_latest_version" {
28+
crypto_key = data.google_kms_crypto_key.my_key.id
29+
}
30+
```
31+
32+
## Argument Reference
33+
34+
The following arguments are supported:
35+
36+
* `crypto_key` - (Required) The `id` of the Google Cloud Platform CryptoKey to which the key version belongs. This is also the `id` field of the
37+
`google_kms_crypto_key` resource/datasource.
38+
39+
* `filter` - (Optional) The filter argument is used to add a filter query parameter that limits which type of cryptoKeyVersion is retrieved as the latest by the data source: ?filter={{filter}}. When no value is provided there is no filtering.
40+
41+
Example filter values if filtering on state.
42+
43+
* `"state:ENABLED"` will retrieve the latest cryptoKeyVersion that has the state "ENABLED".
44+
45+
[See the documentation about using filters](https://cloud.google.com/kms/docs/sorting-and-filtering)
46+
47+
## Attributes Reference
48+
49+
In addition to the arguments listed above, the following computed attributes are
50+
exported:
51+
52+
* `state` - The current state of the latest CryptoKeyVersion. See the [state reference](https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys.cryptoKeyVersions#CryptoKeyVersion.CryptoKeyVersionState) for possible outputs.
53+
54+
* `protection_level` - The ProtectionLevel describing how crypto operations are performed with this CryptoKeyVersion. See the [protection_level reference](https://cloud.google.com/kms/docs/reference/rest/v1/ProtectionLevel) for possible outputs.
55+
56+
* `algorithm` - The CryptoKeyVersionAlgorithm that this CryptoKeyVersion supports. See the [algorithm reference](https://cloud.google.com/kms/docs/reference/rest/v1/CryptoKeyVersionAlgorithm) for possible outputs.
57+
58+
* `public_key` - If the enclosing CryptoKey has purpose `ASYMMETRIC_SIGN` or `ASYMMETRIC_DECRYPT`, this block contains details about the public key associated to this CryptoKeyVersion. Structure is [documented below](#nested_public_key).
59+
60+
<a name="nested_public_key"></a>The `public_key` block, if present, contains:
61+
62+
* `pem` - The public key, encoded in PEM format. For more information, see the RFC 7468 sections for General Considerations and Textual Encoding of Subject Public Key Info.
63+
64+
* `algorithm` - The CryptoKeyVersionAlgorithm that this CryptoKeyVersion supports.

mmv1/third_party/terraform/website/docs/d/kms_crypto_key_versions.html.markdown

+9
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ The following arguments are supported:
3737
* `crypto_key` - (Required) The `id` of the Google Cloud Platform CryptoKey to which the key version belongs. This is also the `id` field of the
3838
`google_kms_crypto_key` resource/datasource.
3939

40+
* `filter` - (Optional) The filter argument is used to add a filter query parameter that limits which versions are retrieved by the data source: ?filter={{filter}}. When no value is provided there is no filtering.
41+
42+
Example filter values if filtering on name. Note: names take the form projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}/cryptoKeys/{{cryptoKey}}/cryptoKeyVersions.
43+
44+
* `"name:my-key-"` will retrieve cryptoKeyVersions that contain "my-key-" anywhere in their name.
45+
* `"name=projects/my-project/locations/global/keyRings/my-key-ring/cryptoKeys/my-key-1/cryptoKeyVersions/my-version-1"` will only retrieve a key with that exact name.
46+
47+
[See the documentation about using filters](https://cloud.google.com/kms/docs/sorting-and-filtering)
48+
4049
## Attributes Reference
4150

4251
In addition to the arguments listed above, the following computed attributes are exported:

0 commit comments

Comments
 (0)