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

Data source aws_secretsmanager_secret_versions #35411

Merged
merged 20 commits into from
Aug 8, 2024
Merged
3 changes: 3 additions & 0 deletions .changelog/35411.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-data-source
aws_secretsmanager_secret_versions
```
23 changes: 23 additions & 0 deletions internal/service/secretsmanager/secret_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,26 @@ func findSecretVersionByTwoPartKey(ctx context.Context, conn *secretsmanager.Cli

return findSecretVersion(ctx, conn, input)
}

func findSecretVersions(ctx context.Context, conn *secretsmanager.Client, input *secretsmanager.ListSecretVersionIdsInput) (*secretsmanager.ListSecretVersionIdsOutput, error) {
output, err := conn.ListSecretVersionIds(ctx, input)

if errs.IsA[*types.ResourceNotFoundException](err) ||
errs.IsAErrorMessageContains[*types.InvalidRequestException](err, "You can’t perform this operation on the secret because it was deleted") ||
errs.IsAErrorMessageContains[*types.InvalidRequestException](err, "You can't perform this operation on the secret because it was marked for deletion") {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil {
return nil, tfresource.NewEmptyResultError(input)
}

return output, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ func TestAccSecretsManagerSecretVersionDataSource_versionStage(t *testing.T) {

func testAccSecretVersionCheckDataSource(datasourceName, resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[datasourceName]
dataSource, ok := s.RootModule().Resources[datasourceName]
if !ok {
return fmt.Errorf("root module has no resource called %s", datasourceName)
return fmt.Errorf("root module has no data source called %s", datasourceName)
}

dataSource, ok := s.RootModule().Resources[resourceName]
resource, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("root module has no resource called %s", resourceName)
}
Expand Down
110 changes: 110 additions & 0 deletions internal/service/secretsmanager/secret_versions_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package secretsmanager

import (
"context"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
)

// @SDKDataSource("aws_secretsmanager_secret_versions", name="Secret Versions")
func dataSourceSecretVersions() *schema.Resource {
return &schema.Resource{
ReadWithoutTimeout: dataSourceSecretVersionsRead,

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"include_deprecated": {
Type: schema.TypeBool,
Optional: true,
},
"max_results": {
Type: schema.TypeInt,
Optional: true,
},
"secret_id": {
Type: schema.TypeString,
Required: true,
},
"versions": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"created_date": {
Type: schema.TypeString,
Computed: true,
},
"last_accessed_date": {
Type: schema.TypeString,
Computed: true,
},
"version_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"version_stages": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
},
}
}

func dataSourceSecretVersionsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).SecretsManagerClient(ctx)

secretId := d.Get("secret_id").(string)

input := &secretsmanager.ListSecretVersionIdsInput{
SecretId: aws.String(secretId),
}

if v, ok := d.GetOk("include_deprecated"); ok {
includeDeprecated := v.(bool)
input.IncludeDeprecated = aws.Bool(includeDeprecated)
}
if v, ok := d.GetOk("max_results"); ok {
maxResults := v.(int)
input.MaxResults = aws.Int32(int32(maxResults))
}

output, err := findSecretVersions(ctx, conn, input)

if err != nil {
return sdkdiag.AppendErrorf(diags, "reading Secrets Manager Secret Versions (%s): %s", secretId, err)
}

d.SetId(secretId)
d.Set("arn", output.ARN)
var versions []interface{}
for _, version := range output.Versions {
versions = append(versions, map[string]interface{}{
"created_date": version.CreatedDate.Format(time.RFC3339),
"last_accessed_date": version.LastAccessedDate.Format(time.RFC3339),
"version_id": version.VersionId,
"version_stages": version.VersionStages,
})
}

d.Set("versions", versions)

return diags
}
214 changes: 214 additions & 0 deletions internal/service/secretsmanager/secret_versions_data_source_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package secretsmanager_test

import (
"fmt"
"strconv"
"testing"

"github.com/YakDriver/regexache"
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
"github.com/hashicorp/terraform-provider-aws/names"
)

func TestAccSecretsManagerSecretVersionsDataSource_basic(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resource1Name := "aws_secretsmanager_secret_version.test1"
resource2Name := "aws_secretsmanager_secret_version.test2"
datasourceName := "data.aws_secretsmanager_secret_versions.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.SecretsManagerEndpointID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccSecretVersionsDataSourceConfig_nonExistent,
ExpectError: regexache.MustCompile(`couldn't find resource`),
},
{
Config: testAccSecretVersionsDataSourceConfig_oneVersion(rName),
Check: resource.ComposeTestCheckFunc(
testAccSecretVersionsCheckDataSource(datasourceName, resource1Name, "", 1),
),
},
{
Config: testAccSecretVersionsDataSourceConfig_twoVersions(rName),
Check: resource.ComposeTestCheckFunc(
testAccSecretVersionsCheckDataSource(datasourceName, resource1Name, resource2Name, 2),
),
},
},
})
}

func TestAccSecretsManagerSecretVersionsDataSource_maxResults(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resource1Name := "aws_secretsmanager_secret_version.test1"
datasourceName := "data.aws_secretsmanager_secret_versions.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.SecretsManagerEndpointID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccSecretVersionsDataSourceConfig_maxResults(rName),
Check: resource.ComposeTestCheckFunc(
testAccSecretVersionsCheckDataSource(datasourceName, resource1Name, "", 1),
),
},
},
})
}

func testAccSecretVersionsCheckDataSource(datasourceName, resource1Name, resource2Name string, versionCount int) resource.TestCheckFunc {
return func(s *terraform.State) error {
dataSource, ok := s.RootModule().Resources[datasourceName]
if !ok {
return fmt.Errorf("root module has no data source called %s", datasourceName)
}

resource1, ok := s.RootModule().Resources[resource1Name]
if !ok {
return fmt.Errorf("root module has no resource called %s", resource1Name)
}

resource2, ok := s.RootModule().Resources[resource2Name]
if resource2Name != "" && !ok {
return fmt.Errorf("root module has no resource called %s", resource2Name)
}

if dataSource.Primary.Attributes["versions.#"] != strconv.Itoa(versionCount) {
return fmt.Errorf(
"versions.# is %s; want %d",
dataSource.Primary.Attributes["versions.#"],
versionCount,
)
}

checkAttributes := func(attrNames []string, resourceAttributes map[string]string) error {
for _, attrName := range attrNames {
if resourceAttributes[attrName] != dataSource.Primary.Attributes[attrName] {
return fmt.Errorf(
"%s is %s; want %s",
attrName,
resourceAttributes[attrName],
dataSource.Primary.Attributes[attrName],
)
}
}
return nil
}

attrNamesResource1 := []string{
"arn",
"secret_id",
"versions.0,created_date",
"versions.0,version_id",
"versions.0,version_stages.#",
"versions.0,last_accessed_date",
}
err := checkAttributes(attrNamesResource1, resource1.Primary.Attributes)
if err != nil {
return err
}

if resource2Name != "" {
attrNamesResource2 := []string{
"arn",
"secret_id",
"versions.1,created_date",
"versions.1,version_id",
"versions.1,version_stages.#",
"versions.1,last_accessed_date",
}
err = checkAttributes(attrNamesResource2, resource2.Primary.Attributes)
if err != nil {
return err
}
}

return nil
}
}

const testAccSecretVersionsDataSourceConfig_nonExistent = `
data "aws_secretsmanager_secret_versions" "test" {
secret_id = "tf-acc-test-does-not-exist"
}
`

func testAccSecretVersionsDataSourceConfig_oneVersion(rName string) string {
return fmt.Sprintf(`
resource "aws_secretsmanager_secret" "test" {
name = "%[1]s"
}

resource "aws_secretsmanager_secret_version" "test1" {
secret_id = aws_secretsmanager_secret.test.id
secret_string = "test-string"
}

data "aws_secretsmanager_secret_versions" "test" {
depends_on = [aws_secretsmanager_secret_version.test1]
secret_id = aws_secretsmanager_secret.test.id
}
`, rName)
}

func testAccSecretVersionsDataSourceConfig_twoVersions(rName string) string {
return fmt.Sprintf(`
resource "aws_secretsmanager_secret" "test" {
name = "%[1]s"
}

resource "aws_secretsmanager_secret_version" "test1" {
secret_id = aws_secretsmanager_secret.test.id
secret_string = "test-string"
}

resource "aws_secretsmanager_secret_version" "test2" {
depends_on = [aws_secretsmanager_secret_version.test1]
secret_id = aws_secretsmanager_secret.test.id
secret_string = "test-string2"
}

data "aws_secretsmanager_secret_versions" "test" {
depends_on = [aws_secretsmanager_secret_version.test1, aws_secretsmanager_secret_version.test2]
secret_id = aws_secretsmanager_secret.test.id
}
`, rName)
}

func testAccSecretVersionsDataSourceConfig_maxResults(rName string) string {
return fmt.Sprintf(`
resource "aws_secretsmanager_secret" "test" {
name = "%[1]s"
}

resource "aws_secretsmanager_secret_version" "test1" {
secret_id = aws_secretsmanager_secret.test.id
secret_string = "test-string"
}

resource "aws_secretsmanager_secret_version" "test2" {
depends_on = [aws_secretsmanager_secret_version.test1]
secret_id = aws_secretsmanager_secret.test.id
secret_string = "test-string2"
}

data "aws_secretsmanager_secret_versions" "test" {
depends_on = [aws_secretsmanager_secret_version.test1, aws_secretsmanager_secret_version.test2]
secret_id = aws_secretsmanager_secret.test.id
max_results = 1
}
`, rName)
}
5 changes: 5 additions & 0 deletions internal/service/secretsmanager/service_package_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading