diff --git a/aws/data_source_aws_iam_policy.go b/aws/data_source_aws_iam_policy.go index 3a2233aac96..938af05e46f 100644 --- a/aws/data_source_aws_iam_policy.go +++ b/aws/data_source_aws_iam_policy.go @@ -1,7 +1,17 @@ package aws import ( + "fmt" + "net/url" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func dataSourceAwsIAMPolicy() *schema.Resource { @@ -40,6 +50,105 @@ func dataSourceAwsIAMPolicy() *schema.Resource { } func dataSourceAwsIAMPolicyRead(d *schema.ResourceData, meta interface{}) error { - d.SetId(d.Get("arn").(string)) - return resourceAwsIamPolicyRead(d, meta) + conn := meta.(*AWSClient).iamconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + arn := d.Get("arn").(string) + + input := &iam.GetPolicyInput{ + PolicyArn: aws.String(arn), + } + + // Handle IAM eventual consistency + var output *iam.GetPolicyOutput + err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError { + var err error + output, err = conn.GetPolicy(input) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + output, err = conn.GetPolicy(input) + } + + if err != nil { + return fmt.Errorf("error reading IAM policy %s: %w", arn, err) + } + + if output == nil || output.Policy == nil { + return fmt.Errorf("error reading IAM policy %s: empty output", arn) + } + + policy := output.Policy + + d.SetId(aws.StringValue(policy.Arn)) + + d.Set("arn", policy.Arn) + d.Set("description", policy.Description) + d.Set("name", policy.PolicyName) + d.Set("path", policy.Path) + d.Set("policy_id", policy.PolicyId) + + if err := d.Set("tags", keyvaluetags.IamKeyValueTags(policy.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + // Retrieve policy + + policyVersionInput := &iam.GetPolicyVersionInput{ + PolicyArn: aws.String(arn), + VersionId: policy.DefaultVersionId, + } + + // Handle IAM eventual consistency + var policyVersionOutput *iam.GetPolicyVersionOutput + err = resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError { + var err error + policyVersionOutput, err = conn.GetPolicyVersion(policyVersionInput) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + policyVersionOutput, err = conn.GetPolicyVersion(policyVersionInput) + } + + if err != nil { + return fmt.Errorf("error reading IAM Policy (%s) version: %w", arn, err) + } + + if policyVersionOutput == nil || policyVersionOutput.PolicyVersion == nil { + return fmt.Errorf("error reading IAM Policy (%s) version: empty output", arn) + } + + policyVersion := policyVersionOutput.PolicyVersion + + var policyDocument string + if policyVersion != nil { + policyDocument, err = url.QueryUnescape(aws.StringValue(policyVersion.Document)) + if err != nil { + return fmt.Errorf("error parsing IAM Policy (%s) document: %w", arn, err) + } + } + + d.Set("policy", policyDocument) + + return nil } diff --git a/aws/resource_aws_iam_policy.go b/aws/resource_aws_iam_policy.go index ef7c52a25ff..e0536cb9874 100644 --- a/aws/resource_aws_iam_policy.go +++ b/aws/resource_aws_iam_policy.go @@ -8,11 +8,13 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsIamPolicy() *schema.Resource { @@ -99,7 +101,7 @@ func resourceAwsIamPolicyCreate(d *schema.ResourceData, meta interface{}) error response, err := conn.CreatePolicy(request) if err != nil { - return fmt.Errorf("Error creating IAM policy %s: %w", name, err) + return fmt.Errorf("error creating IAM policy %s: %w", name, err) } d.SetId(aws.StringValue(response.Policy.Arn)) @@ -111,18 +113,17 @@ func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).iamconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - getPolicyRequest := &iam.GetPolicyInput{ + input := &iam.GetPolicyInput{ PolicyArn: aws.String(d.Id()), } - log.Printf("[DEBUG] Getting IAM Policy: %s", getPolicyRequest) // Handle IAM eventual consistency var getPolicyResponse *iam.GetPolicyOutput err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError { var err error - getPolicyResponse, err = conn.GetPolicy(getPolicyRequest) + getPolicyResponse, err = conn.GetPolicy(input) - if d.IsNewResource() && isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") { + if d.IsNewResource() && tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { return resource.RetryableError(err) } @@ -132,17 +133,19 @@ func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error { return nil }) - if isResourceTimeoutError(err) { - getPolicyResponse, err = conn.GetPolicy(getPolicyRequest) + + if tfresource.TimedOut(err) { + getPolicyResponse, err = conn.GetPolicy(input) } - if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") { + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { log.Printf("[WARN] IAM Policy (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("Error reading IAM policy %s: %w", d.Id(), err) + return fmt.Errorf("error reading IAM policy %s: %w", d.Id(), err) } if getPolicyResponse == nil || getPolicyResponse.Policy == nil { @@ -151,14 +154,15 @@ func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error { return nil } - policyRes := getPolicyResponse.Policy - d.Set("arn", policyRes.Arn) - d.Set("description", policyRes.Description) - d.Set("name", policyRes.PolicyName) - d.Set("path", policyRes.Path) - d.Set("policy_id", policyRes.PolicyId) + policy := getPolicyResponse.Policy + + d.Set("arn", policy.Arn) + d.Set("description", policy.Description) + d.Set("name", policy.PolicyName) + d.Set("path", policy.Path) + d.Set("policy_id", policy.PolicyId) - if err := d.Set("tags", keyvaluetags.IamKeyValueTags(policyRes.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + if err := d.Set("tags", keyvaluetags.IamKeyValueTags(policy.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return fmt.Errorf("error setting tags: %w", err) } @@ -166,9 +170,8 @@ func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error { getPolicyVersionRequest := &iam.GetPolicyVersionInput{ PolicyArn: aws.String(d.Id()), - VersionId: policyRes.DefaultVersionId, + VersionId: policy.DefaultVersionId, } - log.Printf("[DEBUG] Getting IAM Policy Version: %s", getPolicyVersionRequest) // Handle IAM eventual consistency var getPolicyVersionResponse *iam.GetPolicyVersionOutput @@ -176,7 +179,7 @@ func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error { var err error getPolicyVersionResponse, err = conn.GetPolicyVersion(getPolicyVersionRequest) - if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") { + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { return resource.RetryableError(err) } @@ -186,29 +189,31 @@ func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error { return nil }) - if isResourceTimeoutError(err) { + + if tfresource.TimedOut(err) { getPolicyVersionResponse, err = conn.GetPolicyVersion(getPolicyVersionRequest) } - if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") { + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { log.Printf("[WARN] IAM Policy (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("Error reading IAM policy version %s: %w", d.Id(), err) + return fmt.Errorf("error reading IAM policy version %s: %w", d.Id(), err) } - policy := "" + var policyDocument string if getPolicyVersionResponse != nil && getPolicyVersionResponse.PolicyVersion != nil { var err error - policy, err = url.QueryUnescape(aws.StringValue(getPolicyVersionResponse.PolicyVersion.Document)) + policyDocument, err = url.QueryUnescape(aws.StringValue(getPolicyVersionResponse.PolicyVersion.Document)) if err != nil { - return fmt.Errorf("error parsing policy: %w", err) + return fmt.Errorf("error parsing IAM policy (%s) document: %w", d.Id(), err) } } - d.Set("policy", policy) + d.Set("policy", policyDocument) return nil } @@ -229,7 +234,7 @@ func resourceAwsIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error } if _, err := conn.CreatePolicyVersion(request); err != nil { - return fmt.Errorf("Error updating IAM policy %s: %w", d.Id(), err) + return fmt.Errorf("error updating IAM policy %s: %w", d.Id(), err) } } @@ -255,11 +260,14 @@ func resourceAwsIamPolicyDelete(d *schema.ResourceData, meta interface{}) error PolicyArn: aws.String(d.Id()), } - if _, err := conn.DeletePolicy(request); err != nil { - if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") { - return nil - } - return fmt.Errorf("Error deleting IAM policy %s: %w", d.Id(), err) + _, err := conn.DeletePolicy(request) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting IAM policy %s: %w", d.Id(), err) } return nil