Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to manage items within a generic secret #2394

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

FEATURES:

* Add resource `vault_generic_secret_item` to allow a more granular management of Vault generic secrets ([#2394](https://github.com/hashicorp/terraform-provider-vault/pull/
* Update `vault_pki_secret_backend_root_cert` and `vault_pki_secret_backend_root_sign_intermediate` to support the new fields for the name constraints extension. Requires Vault 1.19+ ([#2396](https://github.com/hashicorp/terraform-provider-vault/pull/2396)).
* Update `vault_pki_secret_backend_issuer` resource with the new issuer configuration fields to control certificate verification. Requires Vault Enterprise 1.19+ ([#2400](https://github.com/hashicorp/terraform-provider-vault/pull/2400)).
* Add support for certificate revocation with `revoke_with_key` in `vault_pki_secret_backend_cert` ([#2242](https://github.com/hashicorp/terraform-provider-vault/pull/2242))
Expand Down
2 changes: 2 additions & 0 deletions internal/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const (
FieldLeaseRenewable = "lease_renewable"
FieldDepth = "depth"
FieldDataJSON = "data_json"
FieldKey = "key"
FieldValue = "value"
FieldDN = "dn"
FieldRole = "role"
FieldRoles = "roles"
Expand Down
75 changes: 75 additions & 0 deletions vault/data_source_generic_secret_item.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package vault

import (
"fmt"

"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 genericSecretItemDataSource() *schema.Resource {
return &schema.Resource{
Read: provider.ReadWrapper(genericSecretItemDataSourceRead),

Schema: map[string]*schema.Schema{
consts.FieldPath: {
Type: schema.TypeString,
Required: true,
Description: "Full path from which a secret will be read.",
},

consts.FieldKey: {
Type: schema.TypeString,
Required: true,
Description: "Name of the secret item to read.",
},

consts.FieldValue: {
Type: schema.TypeString,
Required: false,
Computed: true,
Description: "Content of the secret item to read.",
Sensitive: true,
},
},
}
}

func genericSecretItemDataSourceRead(d *schema.ResourceData, meta interface{}) error {
client, e := provider.GetClient(d, meta)
if e != nil {
return e
}

path := d.Get(consts.FieldPath).(string)
key := d.Get(consts.FieldKey).(string)

secret, err := versionedSecret(-1, path, client)
if err != nil {
return fmt.Errorf("error reading from Vault: %s", err)
}
if secret == nil {
return fmt.Errorf("no secret found at %q", path)
}

if _, ok := secret.Data[key]; !ok {
return fmt.Errorf("no secret item named %q was found", key)
}

d.SetId(key)

if err := d.Set(consts.FieldKey, key); err != nil {
return err
}

if err := d.Set(consts.FieldValue, secret.Data[d.Get(consts.FieldKey).(string)]); err != nil {
return err
}

return nil
}
167 changes: 167 additions & 0 deletions vault/data_source_generic_secret_item_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// 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-plugin-sdk/v2/terraform"

"github.com/hashicorp/terraform-provider-vault/testutil"
)

func TestDataSourceGenericSecretItem(t *testing.T) {
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories,
PreCheck: func() { testutil.TestAccPreCheck(t) },
Steps: []resource.TestStep{
{
Config: testDataSourceGenericSecretItem_config,
Check: testDataSourceGenericSecretItem_check,
},
},
})
}

func TestDataSourceGenericSecretItem_v2(t *testing.T) {
mount := acctest.RandomWithPrefix("tf-acctest-kv/")
path := acctest.RandomWithPrefix("foo")
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories,
PreCheck: func() { testutil.TestAccPreCheck(t) },
Steps: []resource.TestStep{
{
Config: testDataSourceV2SecretItem_config(mount, path),
Check: testDataSourceGenericSecretItem_check,
},
{
Config: testDataSourceV2SecretItemUpdated_config(mount, path),
Check: testDataSourceGenericSecretItemUpdated_check,
},
},
})
}

func testDataSourceV2SecretItem_config(mount, path string) string {
return fmt.Sprintf(`
resource "vault_mount" "test" {
path = "%s"
type = "kv"
description = "This is an example mount"
options = {
"version" = "2"
}
}

resource "vault_generic_secret_item" "test" {
path = "${vault_mount.test.path}/%s"
key = "foo"
value = "bar"
}

data "vault_generic_secret_item" "test" {
path = vault_generic_secret_item.test.path
key = "foo"
}
`, mount, path)
}

func testDataSourceV2SecretItemUpdated_config(mount, path string) string {
return fmt.Sprintf(`
resource "vault_mount" "test" {
path = "%s"
type = "kv"
description = "This is an example mount"
options = {
"version" = "2"
}
}

resource "vault_generic_secret_item" "test" {
path = "${vault_mount.test.path}/%s"
key = "foo"
value = "baz"
}

data "vault_generic_secret_item" "test" {
path = vault_generic_secret_item.test.path
key = "foo"
}
`, mount, path)
}

var testDataSourceGenericSecretItem_config = `

resource "vault_mount" "v1" {
path = "secretsv1"
type = "kv"
options = {
version = "1"
}
}

resource "vault_generic_secret_item" "test" {
path = "${vault_mount.v1.path}/foo"
key = "foo"
value = "bar"
}

data "vault_generic_secret_item" "test" {
path = vault_generic_secret_item.test.path
key = "foo"
}

`

func testDataSourceGenericSecretItem_check(s *terraform.State) error {
resourceState := s.Modules[0].Resources["data.vault_generic_secret_item.test"]
if resourceState == nil {
return fmt.Errorf("resource not found in state %v", s.Modules[0].Resources)
}

iState := resourceState.Primary

if iState == nil {
return fmt.Errorf("resource has no primary instance")
}

wantKey := "foo"
if got, want := iState.Attributes["key"], wantKey; got != want {
return fmt.Errorf("key contains %s; want %s", got, want)
}

wantVal := "bar"
if got, want := iState.Attributes["value"], wantVal; got != want {
return fmt.Errorf("value contains %s; want %s", got, want)
}

return nil
}

func testDataSourceGenericSecretItemUpdated_check(s *terraform.State) error {
resourceState := s.Modules[0].Resources["data.vault_generic_secret_item.test"]
if resourceState == nil {
return fmt.Errorf("resource not found in state %v", s.Modules[0].Resources)
}

iState := resourceState.Primary
if iState == nil {
return fmt.Errorf("resource has no primary instance")
}

wantKey := "foo"
if got, want := iState.Attributes["key"], wantKey; got != want {
return fmt.Errorf("key contains %s; want %s", got, want)
}

wantVal := "baz"
if got, want := iState.Attributes["value"], wantVal; got != want {
return fmt.Errorf("value contains %s; want %s", got, want)
}

return nil
}
8 changes: 8 additions & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ var (
Resource: UpdateSchemaResource(genericSecretDataSource()),
PathInventory: []string{"/secret/data/{path}"},
},
"vault_generic_secret_item": {
Resource: UpdateSchemaResource(genericSecretItemDataSource()),
PathInventory: []string{"/secret/data/{path}"},
},
"vault_policy_document": {
Resource: UpdateSchemaResource(policyDocumentDataSource()),
PathInventory: []string{"/sys/policy/{name}"},
Expand Down Expand Up @@ -385,6 +389,10 @@ var (
Resource: UpdateSchemaResource(genericSecretResource("vault_generic_secret")),
PathInventory: []string{GenericPath},
},
"vault_generic_secret_item": {
Resource: UpdateSchemaResource(genericSecretItemResource()),
PathInventory: []string{GenericPath},
},
"vault_jwt_auth_backend": {
Resource: UpdateSchemaResource(jwtAuthBackendResource()),
PathInventory: []string{"/auth/jwt/config"},
Expand Down
Loading