From 2173bebed85649388146e9f3d8d38cf2aac4adcb Mon Sep 17 00:00:00 2001 From: Simon Ostendorf Date: Tue, 17 Sep 2024 14:47:37 +0200 Subject: [PATCH] feat: data_source vault_transit_secret_backend_key --- CHANGELOG.md | 4 + .../data_source_transit_secret_backend_key.go | 176 ++++++++++++++++++ ..._source_transit_secret_backend_key_test.go | 67 +++++++ vault/provider.go | 4 + 4 files changed, 251 insertions(+) create mode 100644 vault/data_source_transit_secret_backend_key.go create mode 100644 vault/data_source_transit_secret_backend_key_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fe16ac6a..cae8f8521 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +FEATURES: + +* Add new data source `vault_transit_secret_backend_key`. ([#2327](https://github.com/hashicorp/terraform-provider-vault/pull/2327)) + ## 4.4.0 (Aug 7, 2024) FEATURES: diff --git a/vault/data_source_transit_secret_backend_key.go b/vault/data_source_transit_secret_backend_key.go new file mode 100644 index 000000000..04ca283e7 --- /dev/null +++ b/vault/data_source_transit_secret_backend_key.go @@ -0,0 +1,176 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package vault + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-provider-vault/internal/consts" + "github.com/hashicorp/terraform-provider-vault/internal/provider" +) + +func transitSecretBackendKeyDataSource() *schema.Resource { + return &schema.Resource{ + ReadContext: provider.ReadContextWrapper(readTransitSecretBackendKey), + Schema: map[string]*schema.Schema{ + consts.FieldBackend: { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Full path where transit backend is mounted.", + }, + consts.FieldName: { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Name of the key.", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Specifies the type of the key.", + }, + "deletion_allowed": { + Type: schema.TypeBool, + Computed: true, + Description: "Specifies if the key is allowed to be deleted.", + }, + "derived": { + Type: schema.TypeBool, + Computed: true, + Description: "Specifies if key derivation is used.", + }, + "exportable": { + Type: schema.TypeBool, + Computed: true, + Description: "Speficies if keys is exportable.", + }, + "allow_plaintext_backup": { + Type: schema.TypeBool, + Computed: true, + Description: "Specifies if taking backup of named key in the plaintext format is enabled.", + }, + "keys": { + Type: schema.TypeList, + Computed: true, + Description: "List of key versions in the keyring with the corresponding creation time, name and public key.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "creation_time": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "public_key": { + Type: schema.TypeString, + Computed: true, + }, + "certificate_chain": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "min_decryption_version": { + Type: schema.TypeInt, + Computed: true, + Description: "Minimum key version to use for decryption.", + }, + "min_encryption_version": { + Type: schema.TypeInt, + Computed: true, + Description: "Minimum key version to use for encryption.", + }, + "supports_encryption": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether or not the key supports encryption, based on key type.", + }, + "supports_decryption": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether or not the key supports decryption, based on key type.", + }, + "supports_derivation": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether or not the key supports derivation, based on key type.", + }, + "supports_signing": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether or not the key supports signing, based on key type.", + }, + "imported": { + Type: schema.TypeBool, + Computed: true, + Description: "Specifies if the key is imported.", + }, + }, + } +} + +func readTransitSecretBackendKey(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client, err := provider.GetClient(d, meta) + if err != nil { + return diag.FromErr(err) + } + + backend := d.Get(consts.FieldBackend).(string) + keyName := d.Get(consts.FieldName).(string) + path := fmt.Sprintf("%s/keys/%s", backend, keyName) + + resp, err := client.Logical().ReadWithContext(ctx, path) + if err != nil { + return diag.FromErr(fmt.Errorf("error reading from Vault: %s", err)) + } + log.Printf("[DEBUG] Read %q from Vault", path) + if resp == nil { + return diag.FromErr(fmt.Errorf("no key found at %q", path)) + } + + d.SetId(path) + + keyComputedFields := []string{ + "type", + "deletion_allowed", + "derived", + "exportable", + "allow_plaintext_backup", + "min_decryption_version", + "min_encryption_version", + "supports_encryption", + "supports_decryption", + "supports_derivation", + "supports_signing", + "imported", + } + + for _, k := range keyComputedFields { + if err := d.Set(k, resp.Data[k]); err != nil { + return diag.FromErr(err) + } + } + + var keys []interface{} + for _, keyData := range resp.Data["keys"].(map[string]interface{}) { + keyMap := keyData.(map[string]interface{}) + keys = append(keys, keyMap) + } + + if err := d.Set("keys", keys); err != nil { + return diag.FromErr(err) + } + + return nil +} diff --git a/vault/data_source_transit_secret_backend_key_test.go b/vault/data_source_transit_secret_backend_key_test.go new file mode 100644 index 000000000..8c6f4f262 --- /dev/null +++ b/vault/data_source_transit_secret_backend_key_test.go @@ -0,0 +1,67 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package vault + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-vault/internal/consts" + "github.com/hashicorp/terraform-provider-vault/internal/provider" + "github.com/hashicorp/terraform-provider-vault/testutil" +) + +func TestAccDataSourceTransitSecretKey(t *testing.T) { + backend := acctest.RandomWithPrefix("tf-test-transit-backend") + keyName := acctest.RandomWithPrefix("tf-test-transit-key") + dataName := "data.vault_transit_secret_backend_key.test" + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + PreCheck: func() { + testutil.TestAccPreCheck(t) + SkipIfAPIVersionLT(t, testProvider.Meta(), provider.VaultVersion111) + }, + Steps: []resource.TestStep{ + { + Config: testTransitSecretKeyDataSource(backend, keyName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataName, consts.FieldBackend, backend), + resource.TestCheckResourceAttr(dataName, consts.FieldName, keyName), + resource.TestCheckResourceAttr(dataName, "type", "rsa-4096"), + resource.TestCheckResourceAttr(dataName, "deletion_allowed", "true"), + resource.TestCheckResourceAttr(dataName, "exportable", "true"), + resource.TestCheckResourceAttr(dataName, "keys.0.name", "rsa-4096"), + resource.TestCheckResourceAttr(dataName, "keys.0.certificate_chain", ""), + resource.TestCheckResourceAttrSet(dataName, "keys.0.creation_time"), + resource.TestCheckResourceAttrSet(dataName, "keys.0.public_key"), + ), + }, + }, + }) +} + +func testTransitSecretKeyDataSource(path, keyName string) string { + return fmt.Sprintf(` +resource "vault_mount" "test" { + path = "%s" + type = "transit" + description = "Transit engine mount" +} + +resource "vault_transit_secret_backend_key" "test" { + backend = vault_mount.test.path + name = "%s" + type = "rsa-4096" + deletion_allowed = true + exportable = true +} + +data "vault_transit_secret_backend_key" "test" { + backend = vault_mount.test.path + name = vault_transit_secret_backend_key.test.name +}`, path, keyName) +} diff --git a/vault/provider.go b/vault/provider.go index f64faf555..3b2690d91 100644 --- a/vault/provider.go +++ b/vault/provider.go @@ -195,6 +195,10 @@ var ( Resource: UpdateSchemaResource(transformDecodeDataSource()), PathInventory: []string{"/transform/decode/{role_name}"}, }, + "vault_transit_secret_backend_key": { + Resource: UpdateSchemaResource(transitSecretBackendKeyDataSource()), + PathInventory: []string{"/transit/keys/{key_name}"}, + }, } ResourceRegistry = map[string]*provider.Description{