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

d/aws_iam_policy_document: Fix Failed to marshal plan to json, unsupported attribute "override_json" #36383

Merged
merged 8 commits into from
Mar 14, 2024
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
Loading