From cf7fa9d8e33c4bf437b1fcc2225ed1c8ba73cee0 Mon Sep 17 00:00:00 2001 From: Mauricio Alvarez Leon <65101411+BBBmau@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:30:39 -0700 Subject: [PATCH] `KMS`: add `google_kms_crypto_key_latest_version` data source (#11456) --- .../provider/provider_mmv1_resources.go.erb | 1 + ...ce_google_kms_crypto_key_latest_version.go | 179 ++++++++++++++++++ ...ogle_kms_crypto_key_latest_version_test.go | 76 ++++++++ ...a_source_google_kms_crypto_key_versions.go | 4 +- ...ms_crypto_key_latest_version.html.markdown | 64 +++++++ .../d/kms_crypto_key_versions.html.markdown | 9 + 6 files changed, 330 insertions(+), 3 deletions(-) create mode 100644 mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_latest_version.go create mode 100644 mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_latest_version_test.go create mode 100644 mmv1/third_party/terraform/website/docs/d/kms_crypto_key_latest_version.html.markdown diff --git a/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb b/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb index 67f4b1a5f8a8..b3731e8c3124 100644 --- a/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb +++ b/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb @@ -139,6 +139,7 @@ var handwrittenDatasources = map[string]*schema.Resource{ "google_kms_crypto_key": kms.DataSourceGoogleKmsCryptoKey(), "google_kms_crypto_keys": kms.DataSourceGoogleKmsCryptoKeys(), "google_kms_crypto_key_version": kms.DataSourceGoogleKmsCryptoKeyVersion(), + "google_kms_crypto_key_latest_version": kms.DataSourceGoogleKmsLatestCryptoKeyVersion(), "google_kms_crypto_key_versions": kms.DataSourceGoogleKmsCryptoKeyVersions(), "google_kms_key_ring": kms.DataSourceGoogleKmsKeyRing(), "google_kms_key_rings": kms.DataSourceGoogleKmsKeyRings(), diff --git a/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_latest_version.go b/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_latest_version.go new file mode 100644 index 000000000000..8d98a231b1cc --- /dev/null +++ b/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_latest_version.go @@ -0,0 +1,179 @@ +package kms + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceGoogleKmsLatestCryptoKeyVersion() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGoogleKmsLatestCryptoKeyVersionRead, + Schema: map[string]*schema.Schema{ + "crypto_key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "version": { + Type: schema.TypeInt, + Computed: true, + }, + "algorithm": { + Type: schema.TypeString, + Computed: true, + }, + "protection_level": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + Description: ` + 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. + + Example filter values if filtering on state. + + * "state:ENABLED" will retrieve the latest cryptoKeyVersion that has the state "ENABLED". + + [See the documentation about using filters](https://cloud.google.com/kms/docs/sorting-and-filtering) + `, + }, + "public_key": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "algorithm": { + Type: schema.TypeString, + Computed: true, + }, + "pem": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceGoogleKmsLatestCryptoKeyVersionRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + cryptoKeyId, err := ParseKmsCryptoKeyId(d.Get("crypto_key").(string), config) + if err != nil { + return err + } + + id := fmt.Sprintf("%s/latestCryptoKeyVersion", cryptoKeyId.CryptoKeyId()) + if filter, ok := d.GetOk("filter"); ok { + id += "/filter=" + filter.(string) + } + d.SetId(id) + + versions, err := dataSourceKMSCryptoKeyVersionsList(d, meta, cryptoKeyId.CryptoKeyId(), userAgent) + if err != nil { + return err + } + + // grab latest version + lv := len(versions) - 1 + if lv < 0 { + return fmt.Errorf("No CryptoVersions found in crypto key %s", cryptoKeyId.CryptoKeyId()) + } + + latestVersion := versions[lv].(map[string]interface{}) + + // The google_kms_crypto_key resource and dataset set + // id as the value of name (projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}/cryptoKeys/{{name}}) + // and set name is set as just {{name}}. + + if err := d.Set("name", flattenKmsCryptoKeyVersionName(latestVersion["name"], d)); err != nil { + return fmt.Errorf("Error setting LatestCryptoKeyVersion: %s", err) + } + if err := d.Set("version", flattenKmsCryptoKeyVersionVersion(latestVersion["name"], d)); err != nil { + return fmt.Errorf("Error setting CryptoKeyVersion: %s", err) + } + if err := d.Set("state", flattenKmsCryptoKeyVersionState(latestVersion["state"], d)); err != nil { + return fmt.Errorf("Error setting LatestCryptoKeyVersion: %s", err) + } + if err := d.Set("protection_level", flattenKmsCryptoKeyVersionProtectionLevel(latestVersion["protectionLevel"], d)); err != nil { + return fmt.Errorf("Error setting LatestCryptoKeyVersion: %s", err) + } + if err := d.Set("algorithm", flattenKmsCryptoKeyVersionAlgorithm(latestVersion["algorithm"], d)); err != nil { + return fmt.Errorf("Error setting LatestCryptoKeyVersion: %s", err) + } + + url, err := tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}/cryptoKeyVersions/{{version}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Getting attributes for CryptoKeyVersion: %#v", url) + + url, err = tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Getting purpose of CryptoKey: %#v", url) + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: cryptoKeyId.KeyRingId.Project, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return transport_tpg.HandleDataSourceNotFoundError(err, d, fmt.Sprintf("KmsCryptoKey %q", d.Id()), url) + } + + if res["purpose"] == "ASYMMETRIC_SIGN" || res["purpose"] == "ASYMMETRIC_DECRYPT" { + url, err = tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}/cryptoKeyVersions/{{version}}/publicKey") + if err != nil { + return err + } + log.Printf("[DEBUG] Getting public key of CryptoKeyVersion: %#v", url) + + res, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: cryptoKeyId.KeyRingId.Project, + RawURL: url, + UserAgent: userAgent, + Timeout: d.Timeout(schema.TimeoutRead), + ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.IsCryptoKeyVersionsPendingGeneration}, + }) + + if err != nil { + log.Printf("Error generating public key: %s", err) + return err + } + + if err := d.Set("public_key", flattenKmsCryptoKeyVersionPublicKey(res, d)); err != nil { + return fmt.Errorf("Error setting CryptoKeyVersion public key: %s", err) + } + } + + return nil +} diff --git a/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_latest_version_test.go b/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_latest_version_test.go new file mode 100644 index 000000000000..35da33f7d858 --- /dev/null +++ b/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_latest_version_test.go @@ -0,0 +1,76 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package kms_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(t *testing.T) { + asymSignKey := acctest.BootstrapKMSKeyWithPurpose(t, "ASYMMETRIC_SIGN") + + id := asymSignKey.CryptoKey.Name + "/latestCryptoKeyVersion" + + randomString := acctest.RandString(t, 10) + filterNameFindsNoLatestCryptoKeyVersion := fmt.Sprintf("name:%s", randomString) + filterNameFindEnabledLatestCryptoKeyVersion := "state:enabled" + + findsNoLatestCryptoKeyVersionId := fmt.Sprintf("%s/filter=%s", id, filterNameFindsNoLatestCryptoKeyVersion) + findsEnabledLatestCryptoKeyVersionId := fmt.Sprintf("%s/filter=%s", id, filterNameFindEnabledLatestCryptoKeyVersion) + + context := map[string]interface{}{ + "crypto_key": asymSignKey.CryptoKey.Name, + "filter": "", // Can be overridden using 2nd argument to config funcs + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(context, ""), + // Test will attempt to get the latest version from the list of cryptoKeyVersions, if the latest is not enabled it will return an error + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "id", id), + resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "crypto_key", asymSignKey.CryptoKey.Name), + resource.TestMatchResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "version", regexp.MustCompile("[1-9]+[0-9]*")), + ), + ExpectError: regexp.MustCompile("Error: googleapi: Error 400:"), + }, + { + Config: testAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(context, fmt.Sprintf("filter = \"%s\"", filterNameFindEnabledLatestCryptoKeyVersion)), + Check: resource.ComposeTestCheckFunc( + // This filter should retrieve the latest ENABLED cryptoKeyVersion in the bootstrapped KMS crypto key used by the test + resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "id", findsEnabledLatestCryptoKeyVersionId), + resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "crypto_key", asymSignKey.CryptoKey.Name), + resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "state", "ENABLED"), + ), + }, + { + Config: testAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(context, fmt.Sprintf("filter = \"%s\"", filterNameFindsNoLatestCryptoKeyVersion)), + Check: resource.ComposeTestCheckFunc( + // This filter should retrieve no latest version + resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "id", findsNoLatestCryptoKeyVersionId), + resource.TestCheckResourceAttr("data.google_kms_crypto_key_latest_version.latest_version", "crypto_key", asymSignKey.CryptoKey.Name), + ), + ExpectError: regexp.MustCompile("Error: No CryptoVersions found in crypto key"), + }, + }, + }) +} + +func testAccDataSourceGoogleKmsCryptoKeyLatestVersion_basic(context map[string]interface{}, filter string) string { + context["filter"] = filter + + return acctest.Nprintf(` +data "google_kms_crypto_key_latest_version" "latest_version" { + crypto_key = "%{crypto_key}" + %{filter} +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_versions.go b/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_versions.go index 9439fa02a91c..b91465e7222f 100644 --- a/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_versions.go +++ b/mmv1/third_party/terraform/services/kms/data_source_google_kms_crypto_key_versions.go @@ -1,7 +1,5 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 package kms import ( @@ -161,7 +159,7 @@ func dataSourceGoogleKmsCryptoKeyVersionsRead(d *schema.ResourceData, meta inter func dataSourceKMSCryptoKeyVersionsList(d *schema.ResourceData, meta interface{}, cryptoKeyId string, userAgent string) ([]interface{}, error) { config := meta.(*transport_tpg.Config) - url, err := tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}/cryptoKeyVersions?filter=state=ENABLED") + url, err := tpgresource.ReplaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}/cryptoKeyVersions") if err != nil { return nil, err } diff --git a/mmv1/third_party/terraform/website/docs/d/kms_crypto_key_latest_version.html.markdown b/mmv1/third_party/terraform/website/docs/d/kms_crypto_key_latest_version.html.markdown new file mode 100644 index 000000000000..ab7a055522c0 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/kms_crypto_key_latest_version.html.markdown @@ -0,0 +1,64 @@ +--- +subcategory: "Cloud Key Management Service" +description: |- + Provides access to the latest KMS key version data with Google Cloud KMS. +--- + +# google_kms_crypto_key_latest_version + +Provides access to the latest Google Cloud Platform KMS CryptoKeyVersion in a CryptoKey. For more information see +[the official documentation](https://cloud.google.com/kms/docs/object-hierarchy#key_version) +and +[API](https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys.cryptoKeyVersions). + +## Example Usage + +```hcl +data "google_kms_key_ring" "my_key_ring" { + name = "my-key-ring" + location = "us-central1" +} + +data "google_kms_crypto_key" "my_crypto_key" { + name = "my-crypto-key" + key_ring = data.google_kms_key_ring.my_key_ring.id +} + +data "google_kms_crypto_key_latest_version" "my_crypto_key_latest_version" { + crypto_key = data.google_kms_crypto_key.my_key.id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `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 +`google_kms_crypto_key` resource/datasource. + +* `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. + +Example filter values if filtering on state. + +* `"state:ENABLED"` will retrieve the latest cryptoKeyVersion that has the state "ENABLED". + +[See the documentation about using filters](https://cloud.google.com/kms/docs/sorting-and-filtering) + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are +exported: + +* `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. + +* `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. + +* `algorithm` - The CryptoKeyVersionAlgorithm that this CryptoKeyVersion supports. See the [algorithm reference](https://cloud.google.com/kms/docs/reference/rest/v1/CryptoKeyVersionAlgorithm) for possible outputs. + +* `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). + +The `public_key` block, if present, contains: + +* `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. + +* `algorithm` - The CryptoKeyVersionAlgorithm that this CryptoKeyVersion supports. \ No newline at end of file diff --git a/mmv1/third_party/terraform/website/docs/d/kms_crypto_key_versions.html.markdown b/mmv1/third_party/terraform/website/docs/d/kms_crypto_key_versions.html.markdown index a43594bfa856..d16f9b1c5d4b 100644 --- a/mmv1/third_party/terraform/website/docs/d/kms_crypto_key_versions.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/kms_crypto_key_versions.html.markdown @@ -37,6 +37,15 @@ The following arguments are supported: * `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 `google_kms_crypto_key` resource/datasource. +* `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. + +Example filter values if filtering on name. Note: names take the form projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}/cryptoKeys/{{cryptoKey}}/cryptoKeyVersions. + +* `"name:my-key-"` will retrieve cryptoKeyVersions that contain "my-key-" anywhere in their name. +* `"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. + +[See the documentation about using filters](https://cloud.google.com/kms/docs/sorting-and-filtering) + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: