Skip to content

Commit

Permalink
Merge pull request hashicorp#36383 from hashicorp/b-d/aws_iam_policy_…
Browse files Browse the repository at this point in the history
…document-override_json

[WIP] d/aws_iam_policy_document: Fix `Failed to marshal plan to json, unsupported attribute "override_json"`
  • Loading branch information
ewbankkit authored Mar 14, 2024
2 parents 31891a2 + 503da31 commit 7dcf701
Show file tree
Hide file tree
Showing 34 changed files with 358 additions and 260 deletions.
3 changes: 3 additions & 0 deletions .changelog/36383.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
data-source/aws_iam_policy_document: Fix `Failed to marshal state to json: unsupported attribute "override_json"` and `Failed to marshal state to json: unsupported attribute "source_json"` errors when running `terraform show -json` or `terraform state rm`
```
74 changes: 69 additions & 5 deletions internal/service/iam/access_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
)

// @SDKResource("aws_iam_access_key")
func ResourceAccessKey() *schema.Resource {
// @SDKResource("aws_iam_access_key", name="Access Key")
func resourceAccessKey() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceAccessKeyCreate,
ReadWithoutTimeout: resourceAccessKeyRead,
Expand Down Expand Up @@ -187,8 +190,8 @@ func resourceAccessKeyRead(ctx context.Context, d *schema.ResourceData, meta int
conn := meta.(*conns.AWSClient).IAMConn(ctx)

username := d.Get("user").(string)
key, err := findAccessKeyByTwoPartKey(ctx, conn, username, d.Id())

key, err := FindAccessKey(ctx, conn, username, d.Id())
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] IAM Access Key (%s) for User (%s) not found, removing from state", d.Id(), username)
d.SetId("")
Expand Down Expand Up @@ -242,14 +245,20 @@ func resourceAccessKeyDelete(ctx context.Context, d *schema.ResourceData, meta i
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).IAMConn(ctx)

request := &iam.DeleteAccessKeyInput{
log.Printf("[DEBUG] Deleting IAM Access Key: %s", d.Id())
_, err := conn.DeleteAccessKeyWithContext(ctx, &iam.DeleteAccessKeyInput{
AccessKeyId: aws.String(d.Id()),
UserName: aws.String(d.Get("user").(string)),
})

if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) {
return diags
}

if _, err := conn.DeleteAccessKeyWithContext(ctx, request); err != nil {
if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting IAM Access Key (%s): %s", d.Id(), err)
}

return diags
}

Expand All @@ -264,6 +273,61 @@ func resourceAccessKeyStatusUpdate(ctx context.Context, conn *iam.IAM, d *schema
return err
}

func findAccessKeyByTwoPartKey(ctx context.Context, conn *iam.IAM, username, id string) (*iam.AccessKeyMetadata, error) {
input := &iam.ListAccessKeysInput{
UserName: aws.String(username),
}

return findAccessKey(ctx, conn, input, func(v *iam.AccessKeyMetadata) bool {
return aws.StringValue(v.AccessKeyId) == id
})
}

func findAccessKeysByUser(ctx context.Context, conn *iam.IAM, username string) ([]*iam.AccessKeyMetadata, error) {
input := &iam.ListAccessKeysInput{
UserName: aws.String(username),
}

return findAccessKeys(ctx, conn, input, tfslices.PredicateTrue[*iam.AccessKeyMetadata]())
}

func findAccessKey(ctx context.Context, conn *iam.IAM, input *iam.ListAccessKeysInput, filter tfslices.Predicate[*iam.AccessKeyMetadata]) (*iam.AccessKeyMetadata, error) {
output, err := findAccessKeys(ctx, conn, input, filter)

if err != nil {
return nil, err
}

return tfresource.AssertSinglePtrResult(output)
}

func findAccessKeys(ctx context.Context, conn *iam.IAM, input *iam.ListAccessKeysInput, filter tfslices.Predicate[*iam.AccessKeyMetadata]) ([]*iam.AccessKeyMetadata, error) {
var output []*iam.AccessKeyMetadata

err := conn.ListAccessKeysPagesWithContext(ctx, input, func(page *iam.ListAccessKeysOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, v := range page.AccessKeyMetadata {
if v != nil && filter(v) {
output = append(output, v)
}
}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

return output, err
}

func hmacSignature(key []byte, value []byte) ([]byte, error) {
h := hmac.New(sha256.New, key)
if _, err := h.Write(value); err != nil {
Expand Down
40 changes: 32 additions & 8 deletions internal/service/iam/access_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,30 @@ func TestAccIAMAccessKey_basic(t *testing.T) {
})
}

func TestAccIAMAccessKey_disappears(t *testing.T) {
ctx := acctest.Context(t)
var conf iam.AccessKeyMetadata
resourceName := "aws_iam_access_key.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckAccessKeyDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccAccessKeyConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAccessKeyExists(ctx, resourceName, &conf),
acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceAccessKey(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func TestAccIAMAccessKey_encrypted(t *testing.T) {
ctx := acctest.Context(t)
var conf iam.AccessKeyMetadata
Expand Down Expand Up @@ -145,39 +169,39 @@ func testAccCheckAccessKeyDestroy(ctx context.Context) resource.TestCheckFunc {
continue
}

_, err := tfiam.FindAccessKey(ctx, conn, rs.Primary.Attributes["user"], rs.Primary.ID)
_, err := tfiam.FindAccessKeyByTwoPartKey(ctx, conn, rs.Primary.Attributes["user"], rs.Primary.ID)

if tfresource.NotFound(err) {
return nil
}

if err != nil {
return err
}

return fmt.Errorf("IAM Access Key (%s) still exists", rs.Primary.ID)
}

return nil
}
}

func testAccCheckAccessKeyExists(ctx context.Context, n string, res *iam.AccessKeyMetadata) resource.TestCheckFunc {
func testAccCheckAccessKeyExists(ctx context.Context, n string, v *iam.AccessKeyMetadata) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No Access Key ID is set")
}

conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx)

accessKey, err := tfiam.FindAccessKey(ctx, conn, rs.Primary.Attributes["user"], rs.Primary.ID)
output, err := tfiam.FindAccessKeyByTwoPartKey(ctx, conn, rs.Primary.Attributes["user"], rs.Primary.ID)

if err != nil {
return err
}

*res = *accessKey
*v = *output

return nil
}
Expand Down
20 changes: 7 additions & 13 deletions internal/service/iam/access_keys_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import (
"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/create"
"github.com/hashicorp/terraform-provider-aws/names"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
)

// @SDKDataSource("aws_iam_access_keys")
func DataSourceAccessKeys() *schema.Resource {
// @SDKDataSource("aws_iam_access_keys", name="Access Keys")
func dataSourceAccessKeys() *schema.Resource {
return &schema.Resource{
ReadWithoutTimeout: dataSourceAccessKeysRead,
Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -49,26 +48,21 @@ func DataSourceAccessKeys() *schema.Resource {
}
}

const (
DSNameAccessKeys = "Access Keys Data Source"
)

func dataSourceAccessKeysRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics

conn := meta.(*conns.AWSClient).IAMConn(ctx)

username := d.Get("user").(string)
out, err := FindAccessKeys(ctx, conn, username)
output, err := findAccessKeysByUser(ctx, conn, username)

if err != nil {
return create.AppendDiagError(diags, names.IAM, create.ErrActionReading, DSNameAccessKeys, username, err)
return sdkdiag.AppendErrorf(diags, "reading IAM Access Keys (%s): %s", username, err)
}

d.SetId(username)

if err := d.Set("access_keys", flattenAccessKeys(out)); err != nil {
return create.AppendDiagError(diags, names.IAM, create.ErrActionSetting, DSNameAccessKeys, d.Id(), err)
if err := d.Set("access_keys", flattenAccessKeys(output)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting access_keys: %s", err)
}

return diags
Expand Down
4 changes: 2 additions & 2 deletions internal/service/iam/account_alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
)

// @SDKResource("aws_iam_account_alias")
func ResourceAccountAlias() *schema.Resource {
// @SDKResource("aws_iam_account_alias", name="Account Alias")
func resourceAccountAlias() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceAccountAliasCreate,
ReadWithoutTimeout: resourceAccountAliasRead,
Expand Down
4 changes: 2 additions & 2 deletions internal/service/iam/account_alias_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
)

// @SDKDataSource("aws_iam_account_alias")
func DataSourceAccountAlias() *schema.Resource {
// @SDKDataSource("aws_iam_account_alias", name="Account Alias")
func dataSourceAccountAlias() *schema.Resource {
return &schema.Resource{
ReadWithoutTimeout: dataSourceAccountAliasRead,

Expand Down
38 changes: 24 additions & 14 deletions internal/service/iam/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,33 @@ package iam

// Exports for use in tests only.
var (
ResourceAccessKey = resourceAccessKey
// ResourceAccountAlias = resourceAccountAlias
ResourceAccountPasswordPolicy = resourceAccountPasswordPolicy
ResourceGroup = resourceGroup
ResourceGroupPolicyAttachment = resourceGroupPolicyAttachment
ResourceInstanceProfile = resourceInstanceProfile
ResourceOpenIDConnectProvider = resourceOpenIDConnectProvider
ResourcePolicy = resourcePolicy
ResourcePolicyAttachment = resourcePolicyAttachment
ResourceRolePolicyAttachment = resourceRolePolicyAttachment
ResourceSAMLProvider = resourceSAMLProvider
ResourceServerCertificate = resourceServerCertificate
ResourceServiceLinkedRole = resourceServiceLinkedRole
ResourceUser = resourceUser
ResourceUserLoginProfile = resourceUserLoginProfile
ResourceUserPolicyAttachment = resourceUserPolicyAttachment
ResourceUserSSHKey = resourceUserSSHKey
ResourceVirtualMFADevice = resourceVirtualMFADevice
// ResourceGroupMembership = resourceGroupMembership
ResourceGroupPolicy = resourceGroupPolicy
ResourceGroupPolicyAttachment = resourceGroupPolicyAttachment
ResourceInstanceProfile = resourceInstanceProfile
ResourceOpenIDConnectProvider = resourceOpenIDConnectProvider
ResourcePolicy = resourcePolicy
ResourcePolicyAttachment = resourcePolicyAttachment
ResourceRolePolicy = resourceRolePolicy
ResourceRolePolicyAttachment = resourceRolePolicyAttachment
ResourceSAMLProvider = resourceSAMLProvider
ResourceServerCertificate = resourceServerCertificate
ResourceServiceLinkedRole = resourceServiceLinkedRole
ResourceServiceSpecificCredential = resourceServiceSpecificCredential
ResourceSigningCertificate = resourceSigningCertificate
ResourceUser = resourceUser
ResourceUserGroupMembership = resourceUserGroupMembership
ResourceUserLoginProfile = resourceUserLoginProfile
ResourceUserPolicy = resourceUserPolicy
ResourceUserPolicyAttachment = resourceUserPolicyAttachment
ResourceUserSSHKey = resourceUserSSHKey
ResourceVirtualMFADevice = resourceVirtualMFADevice

FindAccessKeyByTwoPartKey = findAccessKeyByTwoPartKey
FindAccountPasswordPolicy = findAccountPasswordPolicy
FindAttachedGroupPolicies = findAttachedGroupPolicies
FindAttachedGroupPolicyByTwoPartKey = findAttachedGroupPolicyByTwoPartKey
Expand Down
41 changes: 0 additions & 41 deletions internal/service/iam/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,44 +125,3 @@ func FindSigningCertificate(ctx context.Context, conn *iam.IAM, userName, certId

return cert, nil
}

func FindAccessKey(ctx context.Context, conn *iam.IAM, username, id string) (*iam.AccessKeyMetadata, error) {
accessKeys, err := FindAccessKeys(ctx, conn, username)
if err != nil {
return nil, err
}

for _, accessKey := range accessKeys {
if aws.StringValue(accessKey.AccessKeyId) == id {
return accessKey, nil
}
}

return nil, &retry.NotFoundError{}
}

func FindAccessKeys(ctx context.Context, conn *iam.IAM, username string) ([]*iam.AccessKeyMetadata, error) {
input := &iam.ListAccessKeysInput{
UserName: aws.String(username),
}
var output []*iam.AccessKeyMetadata

err := conn.ListAccessKeysPagesWithContext(ctx, input, func(page *iam.ListAccessKeysOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

output = append(output, page.AccessKeyMetadata...)

return !lastPage
})

if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

return output, err
}
4 changes: 2 additions & 2 deletions internal/service/iam/group_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
)

// @SDKDataSource("aws_iam_group")
func DataSourceGroup() *schema.Resource {
// @SDKDataSource("aws_iam_group", name="Group")
func dataSourceGroup() *schema.Resource {
return &schema.Resource{
ReadWithoutTimeout: dataSourceGroupRead,

Expand Down
4 changes: 2 additions & 2 deletions internal/service/iam/group_membership.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
)

// @SDKResource("aws_iam_group_membership")
func ResourceGroupMembership() *schema.Resource {
// @SDKResource("aws_iam_group_membership", name="Group Membership")
func resourceGroupMembership() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceGroupMembershipCreate,
ReadWithoutTimeout: resourceGroupMembershipRead,
Expand Down
4 changes: 2 additions & 2 deletions internal/service/iam/group_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/verify"
)

// @SDKResource("aws_iam_group_policy")
func ResourceGroupPolicy() *schema.Resource {
// @SDKResource("aws_iam_group_policy", name="Group Policy")
func resourceGroupPolicy() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceGroupPolicyPut,
ReadWithoutTimeout: resourceGroupPolicyRead,
Expand Down
Loading

0 comments on commit 7dcf701

Please sign in to comment.