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

iam: ISO-friendly tagging #22544

Merged
merged 6 commits into from
Jan 12, 2022
Merged
Changes from 1 commit
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
Next Next commit
iam: ISO-friendly tagging
YakDriver committed Jan 12, 2022
commit db2ff4e43206a2b98d5af84f64bde8332897da1b
5 changes: 5 additions & 0 deletions internal/service/iam/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package iam

const (
ErrCodeAccessDenied = "AccessDenied"
)
118 changes: 86 additions & 32 deletions internal/service/iam/role.go
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import (
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
awspolicy "github.com/hashicorp/awspolicyequivalence"
@@ -199,25 +200,21 @@ func resourceRoleCreate(d *schema.ResourceData, meta interface{}) error {
request.Tags = Tags(tags.IgnoreAWS())
}

outputRaw, err := tfresource.RetryWhen(
PropagationTimeout,
func() (interface{}, error) {
return conn.CreateRole(request)
},
func(err error) (bool, error) {
if tfawserr.ErrMessageContains(err, iam.ErrCodeMalformedPolicyDocumentException, "Invalid principal in policy") {
return true, err
}
output, err := retryCreateRole(conn, request)

return false, err
},
)
// Some partitions (i.e., ISO) may not support tag-on-create
if request.Tags != nil && meta.(*conns.AWSClient).Partition != endpoints.AwsPartitionID && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, iam.ErrCodeInvalidInputException) || tfawserr.ErrCodeContains(err, iam.ErrCodeServiceFailureException)) {
log.Printf("[WARN] IAM Role (%s) create failed (%s) with tags. Trying create without tags.", d.Id(), err)
request.Tags = nil

output, err = retryCreateRole(conn, request)
}

if err != nil {
return fmt.Errorf("error creating IAM Role (%s): %w", name, err)
}

roleName := aws.StringValue(outputRaw.(*iam.CreateRoleOutput).Role.RoleName)
roleName := aws.StringValue(output.Role.RoleName)

if v, ok := d.GetOk("inline_policy"); ok && v.(*schema.Set).Len() > 0 {
policies := expandRoleInlinePolicies(roleName, v.(*schema.Set).List())
@@ -234,6 +231,22 @@ func resourceRoleCreate(d *schema.ResourceData, meta interface{}) error {
}

d.SetId(roleName)

// Some partitions (i.e., ISO) may not support tag-on-create, attempt tag after create
if request.Tags == nil && len(tags) > 0 && meta.(*conns.AWSClient).Partition != endpoints.AwsPartitionID {
err := roleUpdateTags(conn, d.Id(), nil, tags)

// If default tags only, log and continue. Otherwise, error.
if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, iam.ErrCodeInvalidInputException) || tfawserr.ErrCodeContains(err, iam.ErrCodeServiceFailureException)) {
log.Printf("[WARN] error adding tags after create for IAM Role (%s): %s", d.Id(), err)
return resourceRoleRead(d, meta)
}

if err != nil {
return fmt.Errorf("error creating IAM Role (%s) tags: %w", d.Id(), err)
}
}

return resourceRoleRead(d, meta)
}

@@ -277,17 +290,6 @@ func resourceRoleRead(d *schema.ResourceData, meta interface{}) error {
}
d.Set("unique_id", role.RoleId)

tags := KeyValueTags(role.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)

//lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
}

if err := d.Set("tags_all", tags.Map()); err != nil {
return fmt.Errorf("error setting tags_all: %w", err)
}

assumeRolePolicy, err := url.QueryUnescape(*role.AssumeRolePolicyDocument)
if err != nil {
return err
@@ -318,6 +320,23 @@ func resourceRoleRead(d *schema.ResourceData, meta interface{}) error {
}
d.Set("managed_policy_arns", managedPolicies)

tags := KeyValueTags(role.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)

// Some partitions (i.e., ISO) may not support tagging, giving error
if meta.(*conns.AWSClient).Partition != endpoints.AwsPartitionID && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, iam.ErrCodeInvalidInputException) || tfawserr.ErrCodeContains(err, iam.ErrCodeServiceFailureException)) {
log.Printf("[WARN] Unable to list tags for IAM Role %s: %s", d.Id(), err)
return nil
}

//lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
}

if err := d.Set("tags_all", tags.Map()); err != nil {
return fmt.Errorf("error setting tags_all: %w", err)
}

return nil
}

@@ -401,14 +420,6 @@ func resourceRoleUpdate(d *schema.ResourceData, meta interface{}) error {
}
}

if d.HasChange("tags_all") {
o, n := d.GetChange("tags_all")

if err := roleUpdateTags(conn, d.Id(), o, n); err != nil {
return fmt.Errorf("error updating IAM Role (%s) tags: %s", d.Id(), err)
}
}

if d.HasChange("inline_policy") && inlinePoliciesActualDiff(d) {
roleName := d.Get("name").(string)

@@ -475,6 +486,22 @@ func resourceRoleUpdate(d *schema.ResourceData, meta interface{}) error {
}
}

if d.HasChange("tags_all") {
o, n := d.GetChange("tags_all")

err := roleUpdateTags(conn, d.Id(), o, n)

// Some partitions may not support tagging, giving error
if meta.(*conns.AWSClient).Partition != endpoints.AwsPartitionID && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, iam.ErrCodeInvalidInputException) || tfawserr.ErrCodeContains(err, iam.ErrCodeServiceFailureException)) {
log.Printf("[WARN] Unable to update tags for IAM Role %s: %s", d.Id(), err)
return resourceRoleRead(d, meta)
}

if err != nil {
return fmt.Errorf("error updating IAM Role (%s) tags: %w", d.Id(), err)
}
}

return resourceRoleRead(d, meta)
}

@@ -582,6 +609,33 @@ func deleteRoleInstanceProfiles(conn *iam.IAM, roleName string) error {
return nil
}

func retryCreateRole(conn *iam.IAM, input *iam.CreateRoleInput) (*iam.CreateRoleOutput, error) {
outputRaw, err := tfresource.RetryWhen(
PropagationTimeout,
func() (interface{}, error) {
return conn.CreateRole(input)
},
func(err error) (bool, error) {
if tfawserr.ErrMessageContains(err, iam.ErrCodeMalformedPolicyDocumentException, "Invalid principal in policy") {
return true, err
}

return false, err
},
)

if err != nil {
return nil, err
}

output, ok := outputRaw.(*iam.CreateRoleOutput)
if !ok || output == nil || aws.StringValue(output.Role.RoleName) == "" {
return nil, fmt.Errorf("create IAM role (%s) returned an empty result", aws.StringValue(input.RoleName))
}

return output, err
}

func readRolePolicyAttachments(conn *iam.IAM, roleName string) ([]*string, error) {
managedPolicies := make([]*string, 0)
input := &iam.ListAttachedRolePoliciesInput{