From a2c6c143344970e2533557f3e1af8461838cd51e Mon Sep 17 00:00:00 2001 From: teddylear Date: Mon, 18 Sep 2023 18:57:19 -0400 Subject: [PATCH 001/118] work so far --- internal/service/iam/role_test.go | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index e5d1977728a..bbe78e74cb6 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -51,6 +51,40 @@ func TestAccIAMRole_basic(t *testing.T) { }) } +func TestAccIAMRole_basic_migration_sdk_to_framework(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "": { + VersionConstraint: "5.17.0", + Source: "hashicorp/terraform-provider-aws", + }, + }, + Config: testAccRoleConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttrSet(resourceName, "create_date"), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccRoleConfig_basic(rName), + PlanOnly: true, + }, + }, + }) +} + func TestAccIAMRole_description(t *testing.T) { ctx := acctest.Context(t) var conf iam.Role From 671450e661e66cc1245cfdab7a01dc35d5ccbc37 Mon Sep 17 00:00:00 2001 From: teddylear Date: Mon, 18 Sep 2023 23:04:45 -0400 Subject: [PATCH 002/118] more work so far --- internal/service/iam/role.go | 105 ++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 3b025900e7b..512efadfe5f 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -17,10 +17,17 @@ import ( "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" awspolicy "github.com/hashicorp/awspolicyequivalence" "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "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/structure" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -28,6 +35,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/framework" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -39,8 +47,103 @@ const ( roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength ) + +// TODO: finish this how does this work? + // @SDKResource("aws_iam_role", name="Role") // @Tags +// func newIamRole(context.Context) (resource.ResourceWithConfigure, error) { + // r := &resourceIamRole{} + // // TODO + // // r.create = r.createSecurityGroupRule + // // r.delete = r.deleteSecurityGroupRule + // // r.findByID = r.findSecurityGroupRuleByID + + // return r, nil +// } + +type resourceIamRole struct { + framework.ResourceWithConfigure +} + +func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_iam_role" +} + +// TODO: Update this +func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "arn": schema.StringAttribute{ + Computed: true, + }, + "assume_role_policy": schema.StringAttribute{ + Required: true, + // TODO: finish this, it get complicated + }, + "create_date": schema.StringAttribute{ + Computed: true, + }, + "force_detach_policies": schema.BoolAttribute{ + Optional: true, + Default: booldefault.StaticBool(false), + }, + // TODO: inline policy goes crazy, have to figure what this type should look like + // also read article again + "inline_policy": schema.MapAttribute{ + + }, + "managed_policy_arns": schema.SetAttribute{ + Computed: true, + Optional: true, + ElementType: types.StringType, + // TODO: validate all elements of set are valid arns + // how to do this with helper lib terraform-plugin-framework-validators + }, + "max_session_duration": schema.Int64Attribute{ + Optional: true, + Default: int64default.StaticInt64(3600), + Validators: []validator.Int64{ + int64validator.Between(3600, 43200), + }, + }, + "name": schema.StringAttribute{ + Optional: true, + Computed: true, + // TODO: ForceNew? + // TODO: ConflictsWith? + Validators: []validator.String{ + stringvalidator.LengthAtMost(roleNameMaxLen), + }, + }, + "name_prefix": schema.StringAttribute{ + Optional: true, + Computed: true, + // TODO: ForceNew? + // TODO: ConflictsWith? + Validators: []validator.String{ + stringvalidator.LengthAtMost(roleNamePrefixMaxLen), + }, + }, + }, + } +} + + // NOTE: current schema resource to convert + // "path": { + // Type: schema.TypeString, + // Optional: true, + // Default: "/", + // ForceNew: true, + // ValidateFunc: validation.StringLenBetween(0, 512), + // }, + + +// TODO: Finish this +func (r *resourceIamRole) createIamrole(ctx context.Context, data *resourceSecurityGroupRuleData) (string, error) { + return "", nil +} + func ResourceRole() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceRoleCreate, From 3a41e2d5b5b359411af8455503c39759320e816a Mon Sep 17 00:00:00 2001 From: teddylear Date: Tue, 19 Sep 2023 20:39:01 -0400 Subject: [PATCH 003/118] work so far --- internal/framework/validators/arn.go | 80 ++++++++++++++++++++++++++++ internal/service/iam/role.go | 32 ++++++++--- 2 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 internal/framework/validators/arn.go diff --git a/internal/framework/validators/arn.go b/internal/framework/validators/arn.go new file mode 100644 index 00000000000..5a5b9a54e40 --- /dev/null +++ b/internal/framework/validators/arn.go @@ -0,0 +1,80 @@ +var _ validator.String = arnValidator{} + +type arnValidator struct { +} + +// Description describes the validation in plain text formatting. +func (validator arnValidator) Description(_ context.Context) string { + return "String must be a valid arn" +} + +// MarkdownDescription describes the validation in Markdown formatting. +func (validator arnValidator) MarkdownDescription(ctx context.Context) string { + return validator.Description(ctx) +} + +func (v arnValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { + if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { + return + } + + value := request.ConfigValue.ValueString() + + parsedARN, err := arn.Parse(value) + + if err != nil { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + fmt.Sprintf("(%s) is an invalid ARN: %s", value, err), + fmt.Sprintf("%s", value), + )) + return + } + + if parsedARN.Partition == "" { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + fmt.Sprintf("(%s) is an invalid ARN: missing partition value", value), + fmt.Sprintf("%s", value), + )) + return + } else if !partitionRegexp.MatchString(parsedARN.Partition) { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + fmt.Sprintf("(%s) is an invalid ARN: invalid partition value (expecting to match regular expression: %s)", value, partitionRegexp), + fmt.Sprintf("%s", value), + )) + return + } + + if parsedARN.Region != "" && !regionRegexp.MatchString(parsedARN.Region) { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + fmt.Sprintf("(%s) is an invalid ARN: invalid region value (expecting to match regular expression: %s)", value, regionRegexp), + fmt.Sprintf("%s", value), + )) + return + } + + if parsedARN.AccountID != "" && !accountIDRegexp.MatchString(parsedARN.AccountID) { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + fmt.Sprintf("(%s) is an invalid ARN: invalid account ID value (expecting to match regular expression: %s)", value, accountIDRegexp), + fmt.Sprintf("%s", value), + )) + return + } + + if parsedARN.Resource == "" { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + fmt.Sprintf("(%s) is an invalid ARN: missing resource value", value), + fmt.Sprintf("%s", value), + )) + return + } +} + +func validateArnFramework() validator.String { + return arnValidator{} +} diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 512efadfe5f..2b9b717debb 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -21,8 +21,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -125,20 +126,37 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest stringvalidator.LengthAtMost(roleNamePrefixMaxLen), }, }, + "path": schema.StringAttribute{ + Optional: true, + // TODO: ForceNew + Default: stringdefault.StaticString("/"), + Validators: []validator.String{ + stringvalidator.LengthBetween(0, 512), + }, + }, + "permissions_boundary": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + // verify.ValidARN + }, + }, + "unique_id": schema.StringAttribute{ + Computed: true, + }, + // TODO: tags? }, } } // NOTE: current schema resource to convert - // "path": { - // Type: schema.TypeString, - // Optional: true, - // Default: "/", - // ForceNew: true, - // ValidateFunc: validation.StringLenBetween(0, 512), + // "unique_id": { + // Type: schema.TypeString, + // Computed: true, // }, + + // TODO: Finish this func (r *resourceIamRole) createIamrole(ctx context.Context, data *resourceSecurityGroupRuleData) (string, error) { return "", nil From 7f0cebd3966e66b7ac744be67125539569e9120a Mon Sep 17 00:00:00 2001 From: teddylear Date: Tue, 19 Sep 2023 20:48:19 -0400 Subject: [PATCH 004/118] more work --- internal/framework/validators/arn.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/internal/framework/validators/arn.go b/internal/framework/validators/arn.go index 5a5b9a54e40..9d1493f186f 100644 --- a/internal/framework/validators/arn.go +++ b/internal/framework/validators/arn.go @@ -1,16 +1,29 @@ -var _ validator.String = arnValidator{} +package validators -type arnValidator struct { -} +import ( + "context" + "fmt" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +var accountIDRegexp = regexache.MustCompile(`^(aws|aws-managed|third-party|\d{12}|cw.{10})$`) +var partitionRegexp = regexache.MustCompile(`^aws(-[a-z]+)*$`) +var regionRegexp = regexache.MustCompile(`^[a-z]{2}(-[a-z]+)+-\d$`) + +type arnValidator struct {} // Description describes the validation in plain text formatting. func (validator arnValidator) Description(_ context.Context) string { - return "String must be a valid arn" + return "String must be a valid arn" } // MarkdownDescription describes the validation in Markdown formatting. func (validator arnValidator) MarkdownDescription(ctx context.Context) string { - return validator.Description(ctx) + return validator.Description(ctx) } func (v arnValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { From c360f788123fa0ff5d98bc9949bb36fd5545d7a0 Mon Sep 17 00:00:00 2001 From: teddylear Date: Tue, 19 Sep 2023 21:19:46 -0400 Subject: [PATCH 005/118] more work so far --- internal/framework/validators/arn.go | 70 ++++++---- internal/framework/validators/arn_test.go | 87 ++++++++++++ internal/service/iam/role.go | 162 +++++++++++----------- 3 files changed, 206 insertions(+), 113 deletions(-) create mode 100644 internal/framework/validators/arn_test.go diff --git a/internal/framework/validators/arn.go b/internal/framework/validators/arn.go index 9d1493f186f..2982bc00365 100644 --- a/internal/framework/validators/arn.go +++ b/internal/framework/validators/arn.go @@ -1,8 +1,11 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package validators import ( "context" - "fmt" + "fmt" "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go/aws/arn" @@ -14,80 +17,89 @@ var accountIDRegexp = regexache.MustCompile(`^(aws|aws-managed|third-party|\d{12 var partitionRegexp = regexache.MustCompile(`^aws(-[a-z]+)*$`) var regionRegexp = regexache.MustCompile(`^[a-z]{2}(-[a-z]+)+-\d$`) -type arnValidator struct {} +type arnValidator struct{} // Description describes the validation in plain text formatting. func (validator arnValidator) Description(_ context.Context) string { - return "String must be a valid arn" + return "String must be a valid arn" } // MarkdownDescription describes the validation in Markdown formatting. func (validator arnValidator) MarkdownDescription(ctx context.Context) string { - return validator.Description(ctx) + return validator.Description(ctx) } -func (v arnValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { +func (validator arnValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { return } value := request.ConfigValue.ValueString() + parsedARN, err := arn.Parse(value) - parsedARN, err := arn.Parse(value) - - if err != nil { + if err != nil { response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.Path, + v.Description(ctx), fmt.Sprintf("(%s) is an invalid ARN: %s", value, err), - fmt.Sprintf("%s", value), )) - return - } + return + } - if parsedARN.Partition == "" { + if parsedARN.Partition == "" { response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.Path, + v.Description(ctx), fmt.Sprintf("(%s) is an invalid ARN: missing partition value", value), - fmt.Sprintf("%s", value), )) - return - } else if !partitionRegexp.MatchString(parsedARN.Partition) { + return + } else if !partitionRegexp.MatchString(parsedARN.Partition) { response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.Path, + v.Description(ctx), fmt.Sprintf("(%s) is an invalid ARN: invalid partition value (expecting to match regular expression: %s)", value, partitionRegexp), - fmt.Sprintf("%s", value), )) - return - } + return + } - if parsedARN.Region != "" && !regionRegexp.MatchString(parsedARN.Region) { + if parsedARN.Region != "" && !regionRegexp.MatchString(parsedARN.Region) { response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.Path, fmt.Sprintf("(%s) is an invalid ARN: invalid region value (expecting to match regular expression: %s)", value, regionRegexp), fmt.Sprintf("%s", value), )) - return - } + return + } - if parsedARN.AccountID != "" && !accountIDRegexp.MatchString(parsedARN.AccountID) { + if parsedARN.AccountID != "" && !accountIDRegexp.MatchString(parsedARN.AccountID) { response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.Path, + v.Description(ctx), fmt.Sprintf("(%s) is an invalid ARN: invalid account ID value (expecting to match regular expression: %s)", value, accountIDRegexp), - fmt.Sprintf("%s", value), )) - return - } + return + } - if parsedARN.Resource == "" { + if parsedARN.Resource == "" { response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.Path, + v.Description(ctx), fmt.Sprintf("(%s) is an invalid ARN: missing resource value", value), - fmt.Sprintf("%s", value), )) - return - } + return + } } func validateArnFramework() validator.String { return arnValidator{} } + +// Arn returns a string validator which ensures that any configured +// attribute value: +// +// - Is a string, which represents a valid Arn. +// +// Null (unconfigured) and unknown (known after apply) values are skipped. +func Arn() validator.String { + return arnValidator{} +} diff --git a/internal/framework/validators/arn_test.go b/internal/framework/validators/arn_test.go new file mode 100644 index 00000000000..60c0399d684 --- /dev/null +++ b/internal/framework/validators/arn_test.go @@ -0,0 +1,87 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" +) + +func TestArnValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + val types.String + expectedDiagnostics diag.Diagnostics + } + tests := map[string]testCase{ + "unknown String": { + val: types.StringUnknown(), + }, + "null String": { + val: types.StringNull(), + }, + // "invalid String": { + // val: types.StringValue("test-value"), + // expectedDiagnostics: diag.Diagnostics{ + // diag.NewAttributeErrorDiagnostic( + // path.Root("test"), + // "Invalid Attribute Value", + // "Attribute test value must be a valid IPv4 address, got: test-value", + // ), + // }, + // }, + // "valid IPv4 address": { + // val: types.StringValue("10.2.2.0"), + // }, + // "invalid IPv4 address": { + // val: types.StringValue("10.2.2.256"), + // expectedDiagnostics: diag.Diagnostics{ + // diag.NewAttributeErrorDiagnostic( + // path.Root("test"), + // "Invalid Attribute Value", + // `Attribute test value must be a valid IPv4 address, got: 10.2.2.256`, + // ), + // }, + // }, + // "valid IPv6 address": { + // val: types.StringValue("2001:db8::"), + // expectedDiagnostics: diag.Diagnostics{ + // diag.NewAttributeErrorDiagnostic( + // path.Root("test"), + // "Invalid Attribute Value", + // `Attribute test value must be a valid IPv4 address, got: 2001:db8::`, + // ), + // }, + // }, + } + + for name, test := range tests { + name, test := name, test + t.Run(name, func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + request := validator.StringRequest{ + Path: path.Root("test"), + PathExpression: path.MatchRoot("test"), + ConfigValue: test.val, + } + response := validator.StringResponse{} + fwvalidators.Arn().ValidateString(ctx, request, &response) + + if diff := cmp.Diff(response.Diagnostics, test.expectedDiagnostics); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 2b9b717debb..07809daecbb 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -48,19 +48,18 @@ const ( roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength ) - // TODO: finish this how does this work? // @SDKResource("aws_iam_role", name="Role") // @Tags // func newIamRole(context.Context) (resource.ResourceWithConfigure, error) { - // r := &resourceIamRole{} - // // TODO - // // r.create = r.createSecurityGroupRule - // // r.delete = r.deleteSecurityGroupRule - // // r.findByID = r.findSecurityGroupRuleByID +// r := &resourceIamRole{} +// // TODO +// // r.create = r.createSecurityGroupRule +// // r.delete = r.deleteSecurityGroupRule +// // r.findByID = r.findSecurityGroupRuleByID - // return r, nil +// return r, nil // } type resourceIamRole struct { @@ -75,91 +74,86 @@ func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataR func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "arn": schema.StringAttribute{ - Computed: true, - }, - "assume_role_policy": schema.StringAttribute{ - Required: true, - // TODO: finish this, it get complicated - }, - "create_date": schema.StringAttribute{ - Computed: true, - }, - "force_detach_policies": schema.BoolAttribute{ - Optional: true, - Default: booldefault.StaticBool(false), - }, - // TODO: inline policy goes crazy, have to figure what this type should look like - // also read article again - "inline_policy": schema.MapAttribute{ - - }, - "managed_policy_arns": schema.SetAttribute{ - Computed: true, - Optional: true, + "arn": schema.StringAttribute{ + Computed: true, + }, + "assume_role_policy": schema.StringAttribute{ + Required: true, + // TODO: finish this, it get complicated + }, + "create_date": schema.StringAttribute{ + Computed: true, + }, + "force_detach_policies": schema.BoolAttribute{ + Optional: true, + Default: booldefault.StaticBool(false), + }, + // TODO: inline policy goes crazy, have to figure what this type should look like + // also read article again + "inline_policy": schema.MapAttribute{}, + "managed_policy_arns": schema.SetAttribute{ + Computed: true, + Optional: true, ElementType: types.StringType, - // TODO: validate all elements of set are valid arns - // how to do this with helper lib terraform-plugin-framework-validators - }, - "max_session_duration": schema.Int64Attribute{ - Optional: true, - Default: int64default.StaticInt64(3600), - Validators: []validator.Int64{ - int64validator.Between(3600, 43200), - }, - }, - "name": schema.StringAttribute{ - Optional: true, - Computed: true, - // TODO: ForceNew? - // TODO: ConflictsWith? - Validators: []validator.String{ - stringvalidator.LengthAtMost(roleNameMaxLen), - }, - }, - "name_prefix": schema.StringAttribute{ - Optional: true, - Computed: true, - // TODO: ForceNew? - // TODO: ConflictsWith? - Validators: []validator.String{ - stringvalidator.LengthAtMost(roleNamePrefixMaxLen), - }, - }, - "path": schema.StringAttribute{ - Optional: true, - // TODO: ForceNew - Default: stringdefault.StaticString("/"), - Validators: []validator.String{ - stringvalidator.LengthBetween(0, 512), - }, - }, - "permissions_boundary": schema.StringAttribute{ - Optional: true, - Validators: []validator.String{ - // verify.ValidARN - }, - }, - "unique_id": schema.StringAttribute{ - Computed: true, - }, - // TODO: tags? + // TODO: validate all elements of set are valid arns + // how to do this with helper lib terraform-plugin-framework-validators + }, + "max_session_duration": schema.Int64Attribute{ + Optional: true, + Default: int64default.StaticInt64(3600), + Validators: []validator.Int64{ + int64validator.Between(3600, 43200), + }, + }, + "name": schema.StringAttribute{ + Optional: true, + Computed: true, + // TODO: ForceNew? + // TODO: ConflictsWith? + Validators: []validator.String{ + stringvalidator.LengthAtMost(roleNameMaxLen), + }, + }, + "name_prefix": schema.StringAttribute{ + Optional: true, + Computed: true, + // TODO: ForceNew? + // TODO: ConflictsWith? + Validators: []validator.String{ + stringvalidator.LengthAtMost(roleNamePrefixMaxLen), + }, + }, + "path": schema.StringAttribute{ + Optional: true, + // TODO: ForceNew + Default: stringdefault.StaticString("/"), + Validators: []validator.String{ + stringvalidator.LengthBetween(0, 512), + }, + }, + "permissions_boundary": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + // verify.ValidARN + }, + }, + "unique_id": schema.StringAttribute{ + Computed: true, + }, + // TODO: tags? }, } } - // NOTE: current schema resource to convert - // "unique_id": { - // Type: schema.TypeString, - // Computed: true, - // }, - - - +// NOTE: current schema resource to convert +// "unique_id": { +// Type: schema.TypeString, +// Computed: true, +// }, // TODO: Finish this func (r *resourceIamRole) createIamrole(ctx context.Context, data *resourceSecurityGroupRuleData) (string, error) { - return "", nil + return "", nil } func ResourceRole() *schema.Resource { From daff2f36174723d08da8624bac8f9fc952dd4137 Mon Sep 17 00:00:00 2001 From: teddylear Date: Tue, 19 Sep 2023 21:32:08 -0400 Subject: [PATCH 006/118] feat: add arn framework validator --- internal/framework/validators/arn.go | 101 ++++++++++++++ internal/framework/validators/arn_test.go | 158 ++++++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 internal/framework/validators/arn.go create mode 100644 internal/framework/validators/arn_test.go diff --git a/internal/framework/validators/arn.go b/internal/framework/validators/arn.go new file mode 100644 index 00000000000..e9cdd3ae20f --- /dev/null +++ b/internal/framework/validators/arn.go @@ -0,0 +1,101 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators + +import ( + "context" + "fmt" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +var accountIDRegexp = regexache.MustCompile(`^(aws|aws-managed|third-party|\d{12}|cw.{10})$`) +var partitionRegexp = regexache.MustCompile(`^aws(-[a-z]+)*$`) +var regionRegexp = regexache.MustCompile(`^[a-z]{2}(-[a-z]+)+-\d$`) + +type arnValidator struct{} + +// Description describes the validation in plain text formatting. +func (validator arnValidator) Description(_ context.Context) string { + return "value must be a valid arn" +} + +// MarkdownDescription describes the validation in Markdown formatting. +func (validator arnValidator) MarkdownDescription(ctx context.Context) string { + return validator.Description(ctx) +} + +func (validator arnValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { + if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { + return + } + + value := request.ConfigValue.ValueString() + parsedARN, err := arn.Parse(value) + + if err != nil { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + validator.Description(ctx), + fmt.Sprintf("(%s) is an invalid ARN: %s", value, err), + )) + return + } + + if parsedARN.Partition == "" { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + validator.Description(ctx), + fmt.Sprintf("(%s) is an invalid ARN: missing partition value", value), + )) + return + } else if !partitionRegexp.MatchString(parsedARN.Partition) { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + validator.Description(ctx), + fmt.Sprintf("(%s) is an invalid ARN: invalid partition value (expecting to match regular expression: %s)", value, partitionRegexp), + )) + return + } + + if parsedARN.Region != "" && !regionRegexp.MatchString(parsedARN.Region) { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + validator.Description(ctx), + fmt.Sprintf("(%s) is an invalid ARN: invalid region value (expecting to match regular expression: %s)", value, regionRegexp), + )) + return + } + + if parsedARN.AccountID != "" && !accountIDRegexp.MatchString(parsedARN.AccountID) { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + validator.Description(ctx), + fmt.Sprintf("(%s) is an invalid ARN: invalid account ID value (expecting to match regular expression: %s)", value, accountIDRegexp), + )) + return + } + + if parsedARN.Resource == "" { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + request.Path, + validator.Description(ctx), + fmt.Sprintf("(%s) is an invalid ARN: missing resource value", value), + )) + return + } +} + +// ARN returns a string validator which ensures that any configured +// attribute value: +// +// - Is a string, which represents a valid ARN. +// +// Null (unconfigured) and unknown (known after apply) values are skipped. +func ARN() validator.String { + return arnValidator{} +} diff --git a/internal/framework/validators/arn_test.go b/internal/framework/validators/arn_test.go new file mode 100644 index 00000000000..bb5d6229f54 --- /dev/null +++ b/internal/framework/validators/arn_test.go @@ -0,0 +1,158 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validators_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" +) + +func TestARNValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + val types.String + expectedDiagnostics diag.Diagnostics + } + tests := map[string]testCase{ + "unknown String": { + val: types.StringUnknown(), + }, + "null String": { + val: types.StringNull(), + }, + "Beanstalk": { + val: types.StringValue("arn:aws:elasticbeanstalk:us-east-1:123456789012:environment/My App/MyEnvironment"), + }, + "IAM User": { + val: types.StringValue("arn:aws:iam::123456789012:user/David"), + }, + "Managed IAM policy": { + val: types.StringValue("arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess"), + }, + "ImageBuilder": { + val: types.StringValue("arn:aws:imagebuilder:us-east-1:third-party:component/my-component"), + }, + "RDS": { + val: types.StringValue("arn:aws:rds:eu-west-1:123456789012:db:mysql-db"), + }, + "S3 object": { + val: types.StringValue("arn:aws:s3:::my_corporate_bucket/exampleobject.png"), + }, + "CloudWatch Rule": { + val: types.StringValue("arn:aws:events:us-east-1:319201112229:rule/rule_name"), + }, + "Lambda function": { + val: types.StringValue("arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction"), + }, + "Lambda func qualifier": { + val: types.StringValue("arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction:Qualifier"), + }, + "China EC2 ARN": { + val: types.StringValue("arn:aws-cn:ec2:cn-north-1:123456789012:instance/i-12345678"), + }, + "China S3 ARN": { + val: types.StringValue("arn:aws-cn:s3:::bucket/object"), + }, + "C2S EC2 ARN": { + val: types.StringValue("arn:aws-iso:ec2:us-iso-east-1:123456789012:instance/i-12345678"), + }, + "C2S S3 ARN": { + val: types.StringValue("arn:aws-iso:s3:::bucket/object"), + }, + "SC2S EC2 ARN": { + val: types.StringValue("arn:aws-iso-b:ec2:us-isob-east-1:123456789012:instance/i-12345678"), + }, + "SC2S S3 ARN": { + val: types.StringValue("arn:aws-iso-b:s3:::bucket/object"), + }, + "GovCloud EC2 ARN": { + val: types.StringValue("arn:aws-us-gov:ec2:us-gov-west-1:123456789012:instance/i-12345678"), + }, + "GovCloud S3 ARN": { + val: types.StringValue("arn:aws-us-gov:s3:::bucket/object"), + }, + "Cloudwatch Alarm": { + val: types.StringValue("arn:aws:cloudwatch::cw0000000000:alarm:my-alarm"), + }, + "Invalid prefix 1": { + val: types.StringValue("arn"), + expectedDiagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Value", + `Attribute test value must be a valid arn, got: (arn) is an invalid ARN: arn: invalid prefix`, + ), + }, + }, + "Invalid prefix 2": { + val: types.StringValue("123456789012"), + expectedDiagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Value", + `Attribute test value must be a valid arn, got: (123456789012) is an invalid ARN: arn: invalid prefix`, + ), + }, + }, + "Not enough sections 1": { + val: types.StringValue("arn:aws"), + expectedDiagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Value", + `Attribute test value must be a valid arn, got: (arn:aws) is an invalid ARN: arn: not enough sections`, + ), + }, + }, + "Not enough sections 2": { + val: types.StringValue("arn:aws:logs"), + expectedDiagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Value", + `Attribute test value must be a valid arn, got: (arn:aws:logs) is an invalid ARN: arn: not enough sections`, + ), + }, + }, + "Invalid region value": { + val: types.StringValue("arn:aws:logs:region:*:*"), + expectedDiagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Value", + `Attribute test value must be a valid arn, got: (arn:aws:logs:region:*:*) is an invalid ARN: invalid region value (expecting to match regular expression: ^[a-z]{2}(-[a-z]+)+-\d$)`, + ), + }, + }, + } + + for name, test := range tests { + name, test := name, test + t.Run(name, func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + request := validator.StringRequest{ + Path: path.Root("test"), + PathExpression: path.MatchRoot("test"), + ConfigValue: test.val, + } + response := validator.StringResponse{} + fwvalidators.ARN().ValidateString(ctx, request, &response) + + if diff := cmp.Diff(response.Diagnostics, test.expectedDiagnostics); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} From d7d6ab370f591549a0be028e995a8774b32cd2b2 Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 20 Sep 2023 17:23:05 -0400 Subject: [PATCH 007/118] remove from this branch --- internal/framework/validators/arn.go | 105 ---------------------- internal/framework/validators/arn_test.go | 87 ------------------ 2 files changed, 192 deletions(-) delete mode 100644 internal/framework/validators/arn.go delete mode 100644 internal/framework/validators/arn_test.go diff --git a/internal/framework/validators/arn.go b/internal/framework/validators/arn.go deleted file mode 100644 index 2982bc00365..00000000000 --- a/internal/framework/validators/arn.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package validators - -import ( - "context" - "fmt" - - "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" -) - -var accountIDRegexp = regexache.MustCompile(`^(aws|aws-managed|third-party|\d{12}|cw.{10})$`) -var partitionRegexp = regexache.MustCompile(`^aws(-[a-z]+)*$`) -var regionRegexp = regexache.MustCompile(`^[a-z]{2}(-[a-z]+)+-\d$`) - -type arnValidator struct{} - -// Description describes the validation in plain text formatting. -func (validator arnValidator) Description(_ context.Context) string { - return "String must be a valid arn" -} - -// MarkdownDescription describes the validation in Markdown formatting. -func (validator arnValidator) MarkdownDescription(ctx context.Context) string { - return validator.Description(ctx) -} - -func (validator arnValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { - if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { - return - } - - value := request.ConfigValue.ValueString() - parsedARN, err := arn.Parse(value) - - if err != nil { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - v.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: %s", value, err), - )) - return - } - - if parsedARN.Partition == "" { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - v.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: missing partition value", value), - )) - return - } else if !partitionRegexp.MatchString(parsedARN.Partition) { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - v.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: invalid partition value (expecting to match regular expression: %s)", value, partitionRegexp), - )) - return - } - - if parsedARN.Region != "" && !regionRegexp.MatchString(parsedARN.Region) { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - fmt.Sprintf("(%s) is an invalid ARN: invalid region value (expecting to match regular expression: %s)", value, regionRegexp), - fmt.Sprintf("%s", value), - )) - return - } - - if parsedARN.AccountID != "" && !accountIDRegexp.MatchString(parsedARN.AccountID) { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - v.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: invalid account ID value (expecting to match regular expression: %s)", value, accountIDRegexp), - )) - return - } - - if parsedARN.Resource == "" { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - v.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: missing resource value", value), - )) - return - } -} - -func validateArnFramework() validator.String { - return arnValidator{} -} - -// Arn returns a string validator which ensures that any configured -// attribute value: -// -// - Is a string, which represents a valid Arn. -// -// Null (unconfigured) and unknown (known after apply) values are skipped. -func Arn() validator.String { - return arnValidator{} -} diff --git a/internal/framework/validators/arn_test.go b/internal/framework/validators/arn_test.go deleted file mode 100644 index 60c0399d684..00000000000 --- a/internal/framework/validators/arn_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package validators_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" -) - -func TestArnValidator(t *testing.T) { - t.Parallel() - - type testCase struct { - val types.String - expectedDiagnostics diag.Diagnostics - } - tests := map[string]testCase{ - "unknown String": { - val: types.StringUnknown(), - }, - "null String": { - val: types.StringNull(), - }, - // "invalid String": { - // val: types.StringValue("test-value"), - // expectedDiagnostics: diag.Diagnostics{ - // diag.NewAttributeErrorDiagnostic( - // path.Root("test"), - // "Invalid Attribute Value", - // "Attribute test value must be a valid IPv4 address, got: test-value", - // ), - // }, - // }, - // "valid IPv4 address": { - // val: types.StringValue("10.2.2.0"), - // }, - // "invalid IPv4 address": { - // val: types.StringValue("10.2.2.256"), - // expectedDiagnostics: diag.Diagnostics{ - // diag.NewAttributeErrorDiagnostic( - // path.Root("test"), - // "Invalid Attribute Value", - // `Attribute test value must be a valid IPv4 address, got: 10.2.2.256`, - // ), - // }, - // }, - // "valid IPv6 address": { - // val: types.StringValue("2001:db8::"), - // expectedDiagnostics: diag.Diagnostics{ - // diag.NewAttributeErrorDiagnostic( - // path.Root("test"), - // "Invalid Attribute Value", - // `Attribute test value must be a valid IPv4 address, got: 2001:db8::`, - // ), - // }, - // }, - } - - for name, test := range tests { - name, test := name, test - t.Run(name, func(t *testing.T) { - t.Parallel() - - ctx := context.Background() - - request := validator.StringRequest{ - Path: path.Root("test"), - PathExpression: path.MatchRoot("test"), - ConfigValue: test.val, - } - response := validator.StringResponse{} - fwvalidators.Arn().ValidateString(ctx, request, &response) - - if diff := cmp.Diff(response.Diagnostics, test.expectedDiagnostics); diff != "" { - t.Errorf("unexpected diagnostics difference: %s", diff) - } - }) - } -} From d223cd30a19eee664c0b11099ca0c1b661b9f3b8 Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 27 Sep 2023 19:12:59 -0400 Subject: [PATCH 008/118] force new --- internal/service/iam/role.go | 67 ++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 07809daecbb..07cd6f5b9b5 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -20,10 +20,13 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -37,6 +40,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/framework" + fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -70,6 +74,16 @@ func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataR response.TypeName = "aws_iam_role" } + // Type: schema.TypeString, + // Required: true, + // ValidateFunc: validation.StringIsJSON, + // DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, + // DiffSuppressOnRefresh: true, + // StateFunc: func(v interface{}) string { + // json, _ := structure.NormalizeJsonString(v) + // return json + // }, + // TODO: Update this func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ @@ -79,11 +93,25 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, "assume_role_policy": schema.StringAttribute{ Required: true, + // Validators: []validator.String{ + // // TODO: json validator + // }, // TODO: finish this, it get complicated }, "create_date": schema.StringAttribute{ Computed: true, }, + "description": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(0, 1000), + // TODO: regex does not match? + stringvalidator.RegexMatches( + regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), + `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`, + ), + }, + }, "force_detach_policies": schema.BoolAttribute{ Optional: true, Default: booldefault.StaticBool(false), @@ -95,6 +123,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Computed: true, Optional: true, ElementType: types.StringType, + // TODO: set validator for arn // TODO: validate all elements of set are valid arns // how to do this with helper lib terraform-plugin-framework-validators }, @@ -108,24 +137,34 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "name": schema.StringAttribute{ Optional: true, Computed: true, - // TODO: ForceNew? - // TODO: ConflictsWith? + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, Validators: []validator.String{ stringvalidator.LengthAtMost(roleNameMaxLen), + stringvalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("name_prefix"), + ), }, }, "name_prefix": schema.StringAttribute{ Optional: true, Computed: true, - // TODO: ForceNew? - // TODO: ConflictsWith? + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, Validators: []validator.String{ stringvalidator.LengthAtMost(roleNamePrefixMaxLen), + stringvalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("name"), + ), }, }, "path": schema.StringAttribute{ Optional: true, - // TODO: ForceNew + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, Default: stringdefault.StaticString("/"), Validators: []validator.String{ stringvalidator.LengthBetween(0, 512), @@ -134,22 +173,28 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "permissions_boundary": schema.StringAttribute{ Optional: true, Validators: []validator.String{ - // verify.ValidARN + fwvalidators.ARN(), }, }, "unique_id": schema.StringAttribute{ Computed: true, }, - // TODO: tags? + names.AttrTags: tftags.TagsAttribute(), + names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), }, } } // NOTE: current schema resource to convert -// "unique_id": { -// Type: schema.TypeString, -// Computed: true, -// }, + // "description": { + // Type: schema.TypeString, + // Optional: true, + // ValidateFunc: validation.All( + // validation.StringLenBetween(0, 1000), + // validation.StringDoesNotMatch(regexache.MustCompile("[“‘]"), "cannot contain specially formatted single or double quotes: [“‘]"), + // validation.StringMatch(regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`), + // ), + // }, // TODO: Finish this func (r *resourceIamRole) createIamrole(ctx context.Context, data *resourceSecurityGroupRuleData) (string, error) { From dfb397dd6c9310cb95e96e3c30b31ae854b9f348 Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 27 Sep 2023 19:49:19 -0400 Subject: [PATCH 009/118] more work --- internal/service/iam/role.go | 239 +++++++++-------------------------- 1 file changed, 58 insertions(+), 181 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 07cd6f5b9b5..42bb2fef7a4 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -19,12 +19,12 @@ import ( "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -74,15 +74,15 @@ func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataR response.TypeName = "aws_iam_role" } - // Type: schema.TypeString, - // Required: true, - // ValidateFunc: validation.StringIsJSON, - // DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, - // DiffSuppressOnRefresh: true, - // StateFunc: func(v interface{}) string { - // json, _ := structure.NormalizeJsonString(v) - // return json - // }, +// Type: schema.TypeString, +// Required: true, +// ValidateFunc: validation.StringIsJSON, +// DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, +// DiffSuppressOnRefresh: true, +// StateFunc: func(v interface{}) string { +// json, _ := structure.NormalizeJsonString(v) +// return json +// }, // TODO: Update this func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -94,24 +94,24 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "assume_role_policy": schema.StringAttribute{ Required: true, // Validators: []validator.String{ - // // TODO: json validator - // }, + // // TODO: json validator + // }, // TODO: finish this, it get complicated }, "create_date": schema.StringAttribute{ Computed: true, }, - "description": schema.StringAttribute{ - Optional: true, + "description": schema.StringAttribute{ + Optional: true, Validators: []validator.String{ - stringvalidator.LengthBetween(0, 1000), - // TODO: regex does not match? - stringvalidator.RegexMatches( - regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), - `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`, - ), + stringvalidator.LengthBetween(0, 1000), + // TODO: regex does not match? + stringvalidator.RegexMatches( + regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), + `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`, + ), }, - }, + }, "force_detach_policies": schema.BoolAttribute{ Optional: true, Default: booldefault.StaticBool(false), @@ -123,7 +123,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Computed: true, Optional: true, ElementType: types.StringType, - // TODO: set validator for arn + // TODO: set validator for arn // TODO: validate all elements of set are valid arns // how to do this with helper lib terraform-plugin-framework-validators }, @@ -137,9 +137,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "name": schema.StringAttribute{ Optional: true, Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, Validators: []validator.String{ stringvalidator.LengthAtMost(roleNameMaxLen), stringvalidator.ConflictsWith( @@ -150,9 +150,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "name_prefix": schema.StringAttribute{ Optional: true, Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, Validators: []validator.String{ stringvalidator.LengthAtMost(roleNamePrefixMaxLen), stringvalidator.ConflictsWith( @@ -162,18 +162,18 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, "path": schema.StringAttribute{ Optional: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, Default: stringdefault.StaticString("/"), Validators: []validator.String{ stringvalidator.LengthBetween(0, 512), }, }, "permissions_boundary": schema.StringAttribute{ - Optional: true, + Optional: true, Validators: []validator.String{ - fwvalidators.ARN(), + fwvalidators.ARN(), }, }, "unique_id": schema.StringAttribute{ @@ -185,155 +185,32 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest } } -// NOTE: current schema resource to convert - // "description": { - // Type: schema.TypeString, - // Optional: true, - // ValidateFunc: validation.All( - // validation.StringLenBetween(0, 1000), - // validation.StringDoesNotMatch(regexache.MustCompile("[“‘]"), "cannot contain specially formatted single or double quotes: [“‘]"), - // validation.StringMatch(regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`), - // ), - // }, - -// TODO: Finish this -func (r *resourceIamRole) createIamrole(ctx context.Context, data *resourceSecurityGroupRuleData) (string, error) { - return "", nil +type resourceIamRoleData struct { + // Uni + // ExportOnly types.List `tfsdk:"export_only"` + // ExportTaskIdentifier types.String `tfsdk:"export_task_identifier"` + // FailureCause types.String `tfsdk:"failure_cause"` + // IAMRoleArn types.String `tfsdk:"iam_role_arn"` + // ID types.String `tfsdk:"id"` + // KMSKeyID types.String `tfsdk:"kms_key_id"` + // PercentProgress types.Int64 `tfsdk:"percent_progress"` + // S3BucketName types.String `tfsdk:"s3_bucket_name"` + // S3Prefix types.String `tfsdk:"s3_prefix"` + // SnapshotTime types.String `tfsdk:"snapshot_time"` + // SourceArn types.String `tfsdk:"source_arn"` + // SourceType types.String `tfsdk:"source_type"` + // Status types.String `tfsdk:"status"` + // TaskEndTime types.String `tfsdk:"task_end_time"` + // TaskStartTime types.String `tfsdk:"task_start_time"` + // Timeouts timeouts.Value `tfsdk:"timeouts"` + // WarningMessage types.String `tfsdk:"warning_message"` } -func ResourceRole() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceRoleCreate, - ReadWithoutTimeout: resourceRoleRead, - UpdateWithoutTimeout: resourceRoleUpdate, - DeleteWithoutTimeout: resourceRoleDelete, - - Importer: &schema.ResourceImporter{ - StateContext: resourceRoleImport, - }, - - Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "assume_role_policy": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsJSON, - DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, - DiffSuppressOnRefresh: true, - StateFunc: func(v interface{}) string { - json, _ := structure.NormalizeJsonString(v) - return json - }, - }, - "create_date": { - Type: schema.TypeString, - Computed: true, - }, - "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 1000), - validation.StringDoesNotMatch(regexache.MustCompile("[“‘]"), "cannot contain specially formatted single or double quotes: [“‘]"), - validation.StringMatch(regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`), - ), - }, - "force_detach_policies": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - "inline_policy": { - Type: schema.TypeSet, - Optional: true, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Optional: true, // semantically required but syntactically optional to allow empty inline_policy - ValidateFunc: validation.All( - validation.StringIsNotEmpty, - validRolePolicyName, - ), - }, - "policy": { - Type: schema.TypeString, - Optional: true, // semantically required but syntactically optional to allow empty inline_policy - ValidateFunc: verify.ValidIAMPolicyJSON, - DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, - DiffSuppressOnRefresh: true, - StateFunc: func(v interface{}) string { - json, _ := verify.LegacyPolicyNormalize(v) - return json - }, - }, - }, - }, - DiffSuppressFunc: func(k, _, _ string, d *schema.ResourceData) bool { - if d.Id() == "" { - return false - } - - return !inlinePoliciesActualDiff(d) - }, - }, - "managed_policy_arns": { - Type: schema.TypeSet, - Optional: true, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: verify.ValidARN, - }, - }, - "max_session_duration": { - Type: schema.TypeInt, - Optional: true, - Default: 3600, - ValidateFunc: validation.IntBetween(3600, 43200), - }, - "name": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"name_prefix"}, - ValidateFunc: validResourceName(roleNameMaxLen), - }, - "name_prefix": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"name"}, - ValidateFunc: validResourceName(roleNamePrefixMaxLen), - }, - "path": { - Type: schema.TypeString, - Optional: true, - Default: "/", - ForceNew: true, - ValidateFunc: validation.StringLenBetween(0, 512), - }, - "permissions_boundary": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: verify.ValidARN, - }, - names.AttrTags: tftags.TagsSchema(), - names.AttrTagsAll: tftags.TagsSchemaComputed(), - "unique_id": { - Type: schema.TypeString, - Computed: true, - }, - }, - - CustomizeDiff: verify.SetTagsDiff, - } +// TODO: Finish this +func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().IAMConn(ctx) + assumeRolePolicy, err := structure.NormalizeJsonString(d.Get("assume_role_policy").(string)) + return } func resourceRoleImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { From 6795001a890f716c00559d3bafff5bb506024cba Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 27 Sep 2023 20:09:19 -0400 Subject: [PATCH 010/118] more wokr --- internal/service/iam/role.go | 57 ++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 42bb2fef7a4..26a3672092d 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -50,6 +50,7 @@ import ( const ( roleNameMaxLen = 64 roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength + ResNameIamRole = "IamRole" ) // TODO: finish this how does this work? @@ -186,30 +187,48 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest } type resourceIamRoleData struct { - // Uni - // ExportOnly types.List `tfsdk:"export_only"` - // ExportTaskIdentifier types.String `tfsdk:"export_task_identifier"` - // FailureCause types.String `tfsdk:"failure_cause"` - // IAMRoleArn types.String `tfsdk:"iam_role_arn"` - // ID types.String `tfsdk:"id"` - // KMSKeyID types.String `tfsdk:"kms_key_id"` - // PercentProgress types.Int64 `tfsdk:"percent_progress"` - // S3BucketName types.String `tfsdk:"s3_bucket_name"` - // S3Prefix types.String `tfsdk:"s3_prefix"` - // SnapshotTime types.String `tfsdk:"snapshot_time"` - // SourceArn types.String `tfsdk:"source_arn"` - // SourceType types.String `tfsdk:"source_type"` - // Status types.String `tfsdk:"status"` - // TaskEndTime types.String `tfsdk:"task_end_time"` - // TaskStartTime types.String `tfsdk:"task_start_time"` - // Timeouts timeouts.Value `tfsdk:"timeouts"` - // WarningMessage types.String `tfsdk:"warning_message"` + ARN types.String `tfsdk:"arn"` + AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` + CreateDate types.String `tfsdk:"create_date"` + Description types.String `tfsdk:"description"` + ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` + // TODO: still have to think this one out + InlinePolicy types.Map `tfsdk:"inline_policy"` + ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` + MaxSessionDuration types.Int64 `tfsdk:"max_session_duration"` + Name types.String `tfsdk:"name"` + NamePrefix types.String `tfsdk:"name_prefix"` + Path types.String `tfsdk:"path"` + PermissionsBoundary types.String `tfsdk:"permissions_boundary"` + UniqueId types.String `tfsdk:"unique_id"` + // TODO: tags??? } // TODO: Finish this func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { conn := r.Meta().IAMConn(ctx) - assumeRolePolicy, err := structure.NormalizeJsonString(d.Get("assume_role_policy").(string)) + + var plan resourceIamRoleData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + assumeRolePolicy, err := structure.NormalizeJsonString(plan.AssumeRolePolicy.ValueString()) + + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, plan.AssumeRolePolicy.String(), nil), + errors.New(fmt.Sprintf("assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err)).Error(), + ) + } + + name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) + input := &iam.CreateRoleInput{ + AssumeRolePolicyDocument: aws.String(assumeRolePolicy), + Path: aws.String(plan.Path.ValueString()), + RoleName: aws.String(name), + Tags: getTagsIn(ctx), + } return } From 539b8f29aaf59ffb5e0931333058ad500ca45f2c Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 27 Sep 2023 20:13:40 -0400 Subject: [PATCH 011/118] more steps --- internal/service/iam/role.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 26a3672092d..b31f8cb060b 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -229,6 +229,20 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, RoleName: aws.String(name), Tags: getTagsIn(ctx), } + + if !plan.Description.IsNull() { + input.Description = aws.String(plan.Description.ValueString()) + } + + if !plan.MaxSessionDuration.IsNull() { + input.MaxSessionDuration = aws.Int64(plan.MaxSessionDuration.ValueInt64()) + } + + if !plan.PermissionsBoundary.IsNull() { + input.PermissionsBoundary = aws.String(plan.PermissionsBoundary.ValueString()) + } + + output, err := retryCreateRole(ctx, conn, input) return } From 583fdcf5c90c6509159b2423b3116d793dc79644 Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 27 Sep 2023 20:38:18 -0400 Subject: [PATCH 012/118] more --- internal/service/iam/role.go | 65 +++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index b31f8cb060b..afa84ed6a60 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -33,12 +33,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" "github.com/hashicorp/terraform-provider-aws/internal/framework" fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -139,7 +138,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Optional: true, Computed: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), + stringplanmodifier.RequiresReplaceIfConfigured(), }, Validators: []validator.String{ stringvalidator.LengthAtMost(roleNameMaxLen), @@ -152,7 +151,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Optional: true, Computed: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), + stringplanmodifier.RequiresReplaceIfConfigured(), }, Validators: []validator.String{ stringvalidator.LengthAtMost(roleNamePrefixMaxLen), @@ -164,7 +163,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "path": schema.StringAttribute{ Optional: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), + stringplanmodifier.RequiresReplaceIfConfigured(), }, Default: stringdefault.StaticString("/"), Validators: []validator.String{ @@ -220,6 +219,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, plan.AssumeRolePolicy.String(), nil), errors.New(fmt.Sprintf("assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err)).Error(), ) + return } name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) @@ -243,7 +243,56 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } output, err := retryCreateRole(ctx, conn, input) - return + + // TODO: So this needs tags... do we need on resourceIamRoleData? + // if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + // input.Tags = nil + + // output, err = retryCreateRole(ctx, conn, input) + // } + + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + err.Error(), + ) + return + } + + roleName := aws.StringValue(output.Role.RoleName) + + // TODO: has to figure this out because typing of inline policies + // if !plan.InlinePolicy.IsNull() && len(plan.InlinePolicy.Elements()) > 0 { + // policies := expandRoleInlinePolicies(roleName, v.(*schema.Set).List()) + // if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { + // resp.Diagnostics.AddError( + // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + // err.Error(), + // ) + // return + // } + // } + + if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { + managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) + if err := r.addRoleManagedPolicies(ctx, roleName, managedPolicies); err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + err.Error(), + ) + return + } + } + + // TODO: do something with this? + // some resources have been created but not all attributes + // d.SetId(roleName) + + // last steps + state := plan + // TODO: do we need this? + // state.refreshFromOutput(ctx, out) + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) } func resourceRoleImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { @@ -936,8 +985,8 @@ func addRoleInlinePolicies(ctx context.Context, policies []*iam.PutRolePolicyInp return errs.ErrorOrNil() } -func addRoleManagedPolicies(ctx context.Context, roleName string, policies []*string, meta interface{}) error { - conn := meta.(*conns.AWSClient).IAMConn(ctx) +func (r resourceIamRole) addRoleManagedPolicies(ctx context.Context, roleName string, policies []*string) error { + conn := r.Meta().IAMConn(ctx) var errs *multierror.Error for _, arn := range policies { From c241d149c14f03a38dda294aa1983d5392f8810b Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 27 Sep 2023 20:50:48 -0400 Subject: [PATCH 013/118] more --- internal/service/iam/role.go | 42 ++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index afa84ed6a60..efaa436cc2e 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -49,7 +49,7 @@ import ( const ( roleNameMaxLen = 64 roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength - ResNameIamRole = "IamRole" + ResNameIamRole = "IAM Role" ) // TODO: finish this how does this work? @@ -74,16 +74,6 @@ func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataR response.TypeName = "aws_iam_role" } -// Type: schema.TypeString, -// Required: true, -// ValidateFunc: validation.StringIsJSON, -// DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, -// DiffSuppressOnRefresh: true, -// StateFunc: func(v interface{}) string { -// json, _ := structure.NormalizeJsonString(v) -// return json -// }, - // TODO: Update this func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ @@ -200,7 +190,8 @@ type resourceIamRoleData struct { Path types.String `tfsdk:"path"` PermissionsBoundary types.String `tfsdk:"permissions_boundary"` UniqueId types.String `tfsdk:"unique_id"` - // TODO: tags??? + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` } // TODO: Finish this @@ -287,10 +278,33 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, // TODO: do something with this? // some resources have been created but not all attributes // d.SetId(roleName) + // state := plan + // // TODO: do we need this? + // // state.refreshFromOutput(ctx, out) + // resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + + // For partitions not supporting tag-on-create, attempt tag after create. + if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { + err := roleCreateTags(ctx, conn, name, tags) + + // TODO: read errors or something + // If default tags only, continue. Otherwise, error. + // if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + // return append(diags, resourceRoleRead(ctx, d, meta)...) + // } + + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, fmt.Sprintf("%s tags", ResNameIamRole), name, nil), + err.Error(), + ) + return + } + } - // last steps + // last steps? state := plan - // TODO: do we need this? + // TODO: do we need something?this? // state.refreshFromOutput(ctx, out) resp.Diagnostics.Append(resp.State.Set(ctx, state)...) } From 090d697e72753552dacb2059d9dba10930993d3e Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 27 Sep 2023 20:51:48 -0400 Subject: [PATCH 014/118] fmt --- internal/service/iam/role.go | 124 +++++++++++++++++------------------ 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index efaa436cc2e..2976d705ef9 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -37,8 +37,8 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" - "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -49,7 +49,7 @@ import ( const ( roleNameMaxLen = 64 roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength - ResNameIamRole = "IAM Role" + ResNameIamRole = "IAM Role" ) // TODO: finish this how does this work? @@ -176,22 +176,22 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest } type resourceIamRoleData struct { - ARN types.String `tfsdk:"arn"` - AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` - CreateDate types.String `tfsdk:"create_date"` - Description types.String `tfsdk:"description"` - ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` - // TODO: still have to think this one out - InlinePolicy types.Map `tfsdk:"inline_policy"` - ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` - MaxSessionDuration types.Int64 `tfsdk:"max_session_duration"` - Name types.String `tfsdk:"name"` - NamePrefix types.String `tfsdk:"name_prefix"` - Path types.String `tfsdk:"path"` - PermissionsBoundary types.String `tfsdk:"permissions_boundary"` - UniqueId types.String `tfsdk:"unique_id"` - Tags types.Map `tfsdk:"tags"` - TagsAll types.Map `tfsdk:"tags_all"` + ARN types.String `tfsdk:"arn"` + AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` + CreateDate types.String `tfsdk:"create_date"` + Description types.String `tfsdk:"description"` + ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` + // TODO: still have to think this one out + InlinePolicy types.Map `tfsdk:"inline_policy"` + ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` + MaxSessionDuration types.Int64 `tfsdk:"max_session_duration"` + Name types.String `tfsdk:"name"` + NamePrefix types.String `tfsdk:"name_prefix"` + Path types.String `tfsdk:"path"` + PermissionsBoundary types.String `tfsdk:"permissions_boundary"` + UniqueId types.String `tfsdk:"unique_id"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` } // TODO: Finish this @@ -205,12 +205,12 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } assumeRolePolicy, err := structure.NormalizeJsonString(plan.AssumeRolePolicy.ValueString()) - if err != nil { + if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, plan.AssumeRolePolicy.String(), nil), errors.New(fmt.Sprintf("assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err)).Error(), - ) - return + ) + return } name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) @@ -221,7 +221,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, Tags: getTagsIn(ctx), } - if !plan.Description.IsNull() { + if !plan.Description.IsNull() { input.Description = aws.String(plan.Description.ValueString()) } @@ -235,76 +235,76 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, output, err := retryCreateRole(ctx, conn, input) - // TODO: So this needs tags... do we need on resourceIamRoleData? - // if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { - // input.Tags = nil + // TODO: So this needs tags... do we need on resourceIamRoleData? + // if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + // input.Tags = nil - // output, err = retryCreateRole(ctx, conn, input) + // output, err = retryCreateRole(ctx, conn, input) // } - if err != nil { + if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), - err.Error(), - ) - return - } - - roleName := aws.StringValue(output.Role.RoleName) - - // TODO: has to figure this out because typing of inline policies - // if !plan.InlinePolicy.IsNull() && len(plan.InlinePolicy.Elements()) > 0 { - // policies := expandRoleInlinePolicies(roleName, v.(*schema.Set).List()) - // if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { - // resp.Diagnostics.AddError( - // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), - // err.Error(), - // ) - // return - // } + err.Error(), + ) + return + } + + roleName := aws.StringValue(output.Role.RoleName) + + // TODO: has to figure this out because typing of inline policies + // if !plan.InlinePolicy.IsNull() && len(plan.InlinePolicy.Elements()) > 0 { + // policies := expandRoleInlinePolicies(roleName, v.(*schema.Set).List()) + // if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { + // resp.Diagnostics.AddError( + // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + // err.Error(), + // ) + // return + // } // } if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) if err := r.addRoleManagedPolicies(ctx, roleName, managedPolicies); err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), - err.Error(), - ) - return + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + err.Error(), + ) + return } } - // TODO: do something with this? - // some resources have been created but not all attributes + // TODO: do something with this? + // some resources have been created but not all attributes // d.SetId(roleName) // state := plan - // // TODO: do we need this? + // // TODO: do we need this? // // state.refreshFromOutput(ctx, out) // resp.Diagnostics.Append(resp.State.Set(ctx, state)...) - // For partitions not supporting tag-on-create, attempt tag after create. + // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { err := roleCreateTags(ctx, conn, name, tags) - // TODO: read errors or something + // TODO: read errors or something // If default tags only, continue. Otherwise, error. // if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { - // return append(diags, resourceRoleRead(ctx, d, meta)...) + // return append(diags, resourceRoleRead(ctx, d, meta)...) // } if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, fmt.Sprintf("%s tags", ResNameIamRole), name, nil), - err.Error(), - ) - return + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, fmt.Sprintf("%s tags", ResNameIamRole), name, nil), + err.Error(), + ) + return } } - // last steps? + // last steps? state := plan - // TODO: do we need something?this? + // TODO: do we need something?this? // state.refreshFromOutput(ctx, out) resp.Diagnostics.Append(resp.State.Set(ctx, state)...) } From 659682f39afe12187e5b34396937e79f88d2dbf7 Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 27 Sep 2023 23:50:29 -0400 Subject: [PATCH 015/118] progress --- internal/service/iam/role.go | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 2976d705ef9..e4a0bd92126 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -108,7 +108,11 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, // TODO: inline policy goes crazy, have to figure what this type should look like // also read article again - "inline_policy": schema.MapAttribute{}, + "inline_policy": schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + // TODO: maybe some validation? + }, "managed_policy_arns": schema.SetAttribute{ Computed: true, Optional: true, @@ -194,7 +198,6 @@ type resourceIamRoleData struct { TagsAll types.Map `tfsdk:"tags_all"` } -// TODO: Finish this func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { conn := r.Meta().IAMConn(ctx) @@ -253,16 +256,18 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, roleName := aws.StringValue(output.Role.RoleName) // TODO: has to figure this out because typing of inline policies - // if !plan.InlinePolicy.IsNull() && len(plan.InlinePolicy.Elements()) > 0 { - // policies := expandRoleInlinePolicies(roleName, v.(*schema.Set).List()) - // if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { - // resp.Diagnostics.AddError( - // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), - // err.Error(), - // ) - // return - // } - // } + if !plan.InlinePolicy.IsNull() && !plan.InlinePolicy.IsUnknown() { + inline_policies_map := make(map[string]string) + plan.InlinePolicy.ElementsAs(ctx, inline_policies_map, false) + policies := expandRoleInlinePolicies(roleName, inline_policies_map) + if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + err.Error(), + ) + return + } + } if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) @@ -955,13 +960,15 @@ func expandRoleInlinePolicy(roleName string, tfMap map[string]interface{}) *iam. return apiObject } -func expandRoleInlinePolicies(roleName string, tfList []interface{}) []*iam.PutRolePolicyInput { - if len(tfList) == 0 { +func expandRoleInlinePolicies(roleName string, tfmap map[string]string) []*iam.PutRolePolicyInput { + if len(tfmap) == 0 { return nil } var apiObjects []*iam.PutRolePolicyInput + // TODO: Loop through map, update lower function + for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) From 8082f535a6e28b5bc9a7173effcacce35aed164f Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:55:54 -0500 Subject: [PATCH 016/118] more work --- internal/service/iam/instance_profile_test.go | 47 +- internal/service/iam/role.go | 800 +----------------- .../iam/role_policy_attachment_test.go | 53 +- internal/service/iam/role_test.go | 48 +- internal/service/iam/service_package_gen.go | 2 + .../rds/cluster_role_association_test.go | 52 +- 6 files changed, 134 insertions(+), 868 deletions(-) diff --git a/internal/service/iam/instance_profile_test.go b/internal/service/iam/instance_profile_test.go index 073833c365c..60f2eb2f700 100644 --- a/internal/service/iam/instance_profile_test.go +++ b/internal/service/iam/instance_profile_test.go @@ -206,29 +206,30 @@ func TestAccIAMInstanceProfile_disappears(t *testing.T) { }) } -func TestAccIAMInstanceProfile_Disappears_role(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.InstanceProfile - resourceName := "aws_iam_instance_profile.test" - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceProfileDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccInstanceProfileConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckInstanceProfileExists(ctx, resourceName, &conf), - acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), "aws_iam_role.test"), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} +// TODO: fix this later +// func TestAccIAMInstanceProfile_Disappears_role(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.InstanceProfile +// resourceName := "aws_iam_instance_profile.test" +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckInstanceProfileDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccInstanceProfileConfig_basic(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckInstanceProfileExists(ctx, resourceName, &conf), +// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), "aws_iam_role.test"), +// ), +// ExpectNonEmptyPlan: true, +// }, +// }, +// }) +// } func testAccCheckInstanceProfileDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index c7078a6d2b8..99cf53bfe12 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -7,16 +7,11 @@ import ( "context" "errors" "fmt" - "log" - "net/url" - "time" "github.com/YakDriver/regexache" "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" - awspolicy "github.com/hashicorp/awspolicyequivalence" - "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" @@ -29,25 +24,20 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" - "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/errs" - "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" - "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) const ( - roleNameMaxLen = 64 + roleNameMaxLen = 64 + // TODO: what? roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength ResNameIamRole = "IAM Role" ) @@ -256,18 +246,18 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, roleName := aws.StringValue(output.Role.RoleName) // TODO: has to figure this out because typing of inline policies - if !plan.InlinePolicy.IsNull() && !plan.InlinePolicy.IsUnknown() { - inline_policies_map := make(map[string]string) - plan.InlinePolicy.ElementsAs(ctx, inline_policies_map, false) - policies := expandRoleInlinePolicies(roleName, inline_policies_map) - if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), - err.Error(), - ) - return - } - } + // if !plan.InlinePolicy.IsNull() && !plan.InlinePolicy.IsUnknown() { + // inline_policies_map := make(map[string]string) + // plan.InlinePolicy.ElementsAs(ctx, inline_policies_map, false) + // policies := expandRoleInlinePolicies(roleName, inline_policies_map) + // if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { + // resp.Diagnostics.AddError( + // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + // err.Error(), + // ) + // return + // } + // } if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) @@ -314,422 +304,34 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp.Diagnostics.Append(resp.State.Set(ctx, state)...) } -func resourceRoleImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - d.Set("force_detach_policies", false) - return []*schema.ResourceData{d}, nil -} - -func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).IAMConn(ctx) - - assumeRolePolicy, err := structure.NormalizeJsonString(d.Get("assume_role_policy").(string)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err) - } - - name := create.Name(d.Get("name").(string), d.Get("name_prefix").(string)) - input := &iam.CreateRoleInput{ - AssumeRolePolicyDocument: aws.String(assumeRolePolicy), - Path: aws.String(d.Get("path").(string)), - RoleName: aws.String(name), - Tags: getTagsIn(ctx), - } - - if v, ok := d.GetOk("description"); ok { - input.Description = aws.String(v.(string)) - } - - if v, ok := d.GetOk("max_session_duration"); ok { - input.MaxSessionDuration = aws.Int64(int64(v.(int))) - } - - if v, ok := d.GetOk("permissions_boundary"); ok { - input.PermissionsBoundary = aws.String(v.(string)) - } - - output, err := retryCreateRole(ctx, conn, input) - - // Some partitions (e.g. ISO) may not support tag-on-create. - if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { - input.Tags = nil - - output, err = retryCreateRole(ctx, conn, input) - } - - if err != nil { - return sdkdiag.AppendErrorf(diags, "creating IAM Role (%s): %s", name, err) - } - - 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()) - if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { - return sdkdiag.AppendErrorf(diags, "creating IAM Role (%s): %s", name, err) - } - } - - if v, ok := d.GetOk("managed_policy_arns"); ok && v.(*schema.Set).Len() > 0 { - managedPolicies := flex.ExpandStringSet(v.(*schema.Set)) - if err := addRoleManagedPolicies(ctx, roleName, managedPolicies, meta); err != nil { - return sdkdiag.AppendErrorf(diags, "creating IAM Role (%s): %s", name, err) - } - } - - d.SetId(roleName) - - // For partitions not supporting tag-on-create, attempt tag after create. - if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { - err := roleCreateTags(ctx, conn, d.Id(), tags) - - // If default tags only, continue. Otherwise, error. - if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { - return append(diags, resourceRoleRead(ctx, d, meta)...) - } - - if err != nil { - return sdkdiag.AppendErrorf(diags, "setting IAM Role (%s) tags: %s", d.Id(), err) - } - } - - return append(diags, resourceRoleRead(ctx, d, meta)...) -} - -func resourceRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).IAMConn(ctx) - - outputRaw, err := tfresource.RetryWhenNewResourceNotFound(ctx, propagationTimeout, func() (interface{}, error) { - return FindRoleByName(ctx, conn, d.Id()) - }, d.IsNewResource()) - - if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] IAM Role (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } - - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading IAM Role (%s): %s", d.Id(), err) - } - - role := outputRaw.(*iam.Role) - - // occasionally, immediately after a role is created, AWS will give an ARN like AROAQ7SSZBKHREXAMPLE (unique ID) - if role, err = waitRoleARNIsNotUniqueID(ctx, conn, d.Id(), role); err != nil { - return sdkdiag.AppendErrorf(diags, "reading IAM Role (%s): waiting for valid ARN: %s", d.Id(), err) - } - - d.Set("arn", role.Arn) - d.Set("create_date", role.CreateDate.Format(time.RFC3339)) - d.Set("description", role.Description) - d.Set("max_session_duration", role.MaxSessionDuration) - d.Set("name", role.RoleName) - d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(role.RoleName))) - d.Set("path", role.Path) - if role.PermissionsBoundary != nil { - d.Set("permissions_boundary", role.PermissionsBoundary.PermissionsBoundaryArn) - } else { - d.Set("permissions_boundary", nil) - } - d.Set("unique_id", role.RoleId) - - assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - - policyToSet, err := verify.PolicyToSet(d.Get("assume_role_policy").(string), assumeRolePolicy) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - - d.Set("assume_role_policy", policyToSet) - - inlinePolicies, err := readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName), meta) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) - } - - var configPoliciesList []*iam.PutRolePolicyInput - if v := d.Get("inline_policy").(*schema.Set); v.Len() > 0 { - configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), v.List()) - } - - if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { - if err := d.Set("inline_policy", flattenRoleInlinePolicies(inlinePolicies)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting inline_policy: %s", err) - } - } - - policyARNs, err := findRoleAttachedPolicies(ctx, conn, d.Id()) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) - } - d.Set("managed_policy_arns", policyARNs) - - setTagsOut(ctx, role.Tags) - - return diags -} - -func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).IAMConn(ctx) - - if d.HasChange("assume_role_policy") { - assumeRolePolicy, err := structure.NormalizeJsonString(d.Get("assume_role_policy").(string)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err) - } - - input := &iam.UpdateAssumeRolePolicyInput{ - RoleName: aws.String(d.Id()), - PolicyDocument: aws.String(assumeRolePolicy), - } - - _, err = tfresource.RetryWhen(ctx, propagationTimeout, - func() (interface{}, error) { - return conn.UpdateAssumeRolePolicyWithContext(ctx, 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 sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) assume role policy: %s", d.Id(), err) - } - } - - if d.HasChange("description") { - input := &iam.UpdateRoleDescriptionInput{ - RoleName: aws.String(d.Id()), - Description: aws.String(d.Get("description").(string)), - } - - _, err := conn.UpdateRoleDescriptionWithContext(ctx, input) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) description: %s", d.Id(), err) - } - } - - if d.HasChange("max_session_duration") { - input := &iam.UpdateRoleInput{ - RoleName: aws.String(d.Id()), - MaxSessionDuration: aws.Int64(int64(d.Get("max_session_duration").(int))), - } - - _, err := conn.UpdateRoleWithContext(ctx, input) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) MaxSessionDuration: %s", d.Id(), err) - } - } - - if d.HasChange("permissions_boundary") { - permissionsBoundary := d.Get("permissions_boundary").(string) - if permissionsBoundary != "" { - input := &iam.PutRolePermissionsBoundaryInput{ - PermissionsBoundary: aws.String(permissionsBoundary), - RoleName: aws.String(d.Id()), - } - - _, err := conn.PutRolePermissionsBoundaryWithContext(ctx, input) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) permissions boundary: %s", d.Id(), err) - } - } else { - input := &iam.DeleteRolePermissionsBoundaryInput{ - RoleName: aws.String(d.Id()), - } - - _, err := conn.DeleteRolePermissionsBoundaryWithContext(ctx, input) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting IAM Role (%s) permissions boundary: %s", d.Id(), err) - } - } - } - - if d.HasChange("inline_policy") && inlinePoliciesActualDiff(d) { - roleName := d.Get("name").(string) - - o, n := d.GetChange("inline_policy") - - if o == nil { - o = new(schema.Set) - } - - if n == nil { - n = new(schema.Set) - } - - os := o.(*schema.Set) - ns := n.(*schema.Set) - - remove := os.Difference(ns).List() - add := ns.Difference(os).List() - - var policyNames []string - for _, policy := range remove { - tfMap, ok := policy.(map[string]interface{}) - - if !ok { - continue - } - - if v, ok := tfMap["name"].(string); ok && v != "" { - policyNames = append(policyNames, tfMap["name"].(string)) - } - } - if err := deleteRoleInlinePolicies(ctx, conn, roleName, policyNames); err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) - } - - policies := expandRoleInlinePolicies(roleName, add) - if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) - } - } - - if d.HasChange("managed_policy_arns") { - o, n := d.GetChange("managed_policy_arns") - os, ns := o.(*schema.Set), n.(*schema.Set) - add, del := flex.ExpandStringSet(ns.Difference(os)), flex.ExpandStringValueSet(os.Difference(ns)) - - if err := deleteRolePolicyAttachments(ctx, conn, d.Id(), del); err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) - } - - if err := addRoleManagedPolicies(ctx, d.Id(), add, meta); err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) - } - } - - if d.HasChange("tags_all") { - o, n := d.GetChange("tags_all") - - err := roleUpdateTags(ctx, conn, d.Id(), o, n) - - // Some partitions (e.g. ISO) may not support tagging. - if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { - return append(diags, resourceRoleRead(ctx, d, meta)...) - } - - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating tags for IAM Role (%s): %s", d.Id(), err) - } - } - - return append(diags, resourceRoleRead(ctx, d, meta)...) -} - -func resourceRoleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).IAMConn(ctx) - - hasInline := false - if v, ok := d.GetOk("inline_policy"); ok && v.(*schema.Set).Len() > 0 { - hasInline = true - } - - hasManaged := false - if v, ok := d.GetOk("managed_policy_arns"); ok && v.(*schema.Set).Len() > 0 { - hasManaged = true - } - - err := DeleteRole(ctx, conn, d.Id(), d.Get("force_detach_policies").(bool), hasInline, hasManaged) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting IAM Role (%s): %s", d.Id(), err) +// TODO: delete/update/import +func FindRoleByName(ctx context.Context, conn *iam.IAM, name string) (*iam.Role, error) { + input := &iam.GetRoleInput{ + RoleName: aws.String(name), } - return diags + return findRole(ctx, conn, input) } -func DeleteRole(ctx context.Context, conn *iam.IAM, roleName string, forceDetach, hasInline, hasManaged bool) error { - if err := deleteRoleInstanceProfiles(ctx, conn, roleName); err != nil { - return err - } - - if forceDetach || hasManaged { - policyARNs, err := findRoleAttachedPolicies(ctx, conn, roleName) - - if err != nil { - return fmt.Errorf("reading IAM Policies attached to Role (%s): %w", roleName, err) - } - - if err := deleteRolePolicyAttachments(ctx, conn, roleName, policyARNs); err != nil { - return err - } - } - - if forceDetach || hasInline { - inlinePolicies, err := findRolePolicyNames(ctx, conn, roleName) - - if err != nil { - return fmt.Errorf("reading IAM Role (%s) inline policies: %w", roleName, err) - } - - if err := deleteRoleInlinePolicies(ctx, conn, roleName, inlinePolicies); err != nil { - return err - } - } - - input := &iam.DeleteRoleInput{ - RoleName: aws.String(roleName), - } - _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, propagationTimeout, func() (interface{}, error) { - return conn.DeleteRoleWithContext(ctx, input) - }, iam.ErrCodeDeleteConflictException) +func findRole(ctx context.Context, conn *iam.IAM, input *iam.GetRoleInput) (*iam.Role, error) { + output, err := conn.GetRoleWithContext(ctx, input) if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - return nil - } - - return err -} - -func deleteRoleInstanceProfiles(ctx context.Context, conn *iam.IAM, roleName string) error { - instanceProfiles, err := findInstanceProfilesForRole(ctx, conn, roleName) - - if tfresource.NotFound(err) { - return nil + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } } if err != nil { - return fmt.Errorf("reading IAM Instance Profiles for Role (%s): %w", roleName, err) + return nil, err } - var errs []error - - for _, instanceProfile := range instanceProfiles { - instanceProfileName := aws.StringValue(instanceProfile.InstanceProfileName) - input := &iam.RemoveRoleFromInstanceProfileInput{ - InstanceProfileName: aws.String(instanceProfileName), - RoleName: aws.String(roleName), - } - - _, err := conn.RemoveRoleFromInstanceProfileWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - continue - } - - if err != nil { - errs = append(errs, fmt.Errorf("removing IAM Role (%s) from Instance Profile (%s): %w", roleName, instanceProfileName, err)) - } + if output == nil || output.Role == nil { + return nil, tfresource.NewEmptyResultError(input) } - return errors.Join(errs...) + return output.Role, nil } func retryCreateRole(ctx context.Context, conn *iam.IAM, input *iam.CreateRoleInput) (*iam.CreateRoleOutput, error) { @@ -758,258 +360,9 @@ func retryCreateRole(ctx context.Context, conn *iam.IAM, input *iam.CreateRoleIn return output, err } -func FindRoleByName(ctx context.Context, conn *iam.IAM, name string) (*iam.Role, error) { - input := &iam.GetRoleInput{ - RoleName: aws.String(name), - } - - return findRole(ctx, conn, input) -} - -func findRole(ctx context.Context, conn *iam.IAM, input *iam.GetRoleInput) (*iam.Role, error) { - output, err := conn.GetRoleWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - if output == nil || output.Role == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - return output.Role, nil -} - -func findRoleAttachedPolicies(ctx context.Context, conn *iam.IAM, roleName string) ([]string, error) { - input := &iam.ListAttachedRolePoliciesInput{ - RoleName: aws.String(roleName), - } - var output []string - - err := conn.ListAttachedRolePoliciesPagesWithContext(ctx, input, func(page *iam.ListAttachedRolePoliciesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - for _, v := range page.AttachedPolicies { - if v != nil { - output = append(output, aws.StringValue(v.PolicyArn)) - } - } - - return !lastPage - }) - - if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - return output, nil -} - -func findRolePolicyNames(ctx context.Context, conn *iam.IAM, roleName string) ([]string, error) { - input := &iam.ListRolePoliciesInput{ - RoleName: aws.String(roleName), - } - var output []string - - err := conn.ListRolePoliciesPagesWithContext(ctx, input, func(page *iam.ListRolePoliciesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - for _, v := range page.PolicyNames { - if v != nil { - output = append(output, aws.StringValue(v)) - } - } - - return !lastPage - }) - - if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - return output, nil -} - -func deleteRolePolicyAttachments(ctx context.Context, conn *iam.IAM, roleName string, policyARNs []string) error { - var errs []error - - for _, policyARN := range policyARNs { - input := &iam.DetachRolePolicyInput{ - PolicyArn: aws.String(policyARN), - RoleName: aws.String(roleName), - } - - _, err := conn.DetachRolePolicyWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - continue - } - - if err != nil { - errs = append(errs, fmt.Errorf("detaching IAM Policy (%s) from Role (%s): %w", policyARN, roleName, err)) - } - } - - return errors.Join(errs...) -} - -func deleteRoleInlinePolicies(ctx context.Context, conn *iam.IAM, roleName string, policyNames []string) error { - var errs []error - - for _, policyName := range policyNames { - if len(policyName) == 0 { - continue - } - - input := &iam.DeleteRolePolicyInput{ - PolicyName: aws.String(policyName), - RoleName: aws.String(roleName), - } - - _, err := conn.DeleteRolePolicyWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - continue - } - - if err != nil { - errs = append(errs, fmt.Errorf("deleting IAM Role (%s) policy (%s): %w", roleName, policyName, err)) - } - } - - return errors.Join(errs...) -} - -func flattenRoleInlinePolicy(apiObject *iam.PutRolePolicyInput) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - tfMap["name"] = aws.StringValue(apiObject.PolicyName) - tfMap["policy"] = aws.StringValue(apiObject.PolicyDocument) - - return tfMap -} - -func flattenRoleInlinePolicies(apiObjects []*iam.PutRolePolicyInput) []interface{} { - if len(apiObjects) == 0 { - return nil - } - - var tfList []interface{} - - for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - - tfList = append(tfList, flattenRoleInlinePolicy(apiObject)) - } - - return tfList -} - -func expandRoleInlinePolicy(roleName string, tfMap map[string]interface{}) *iam.PutRolePolicyInput { - if tfMap == nil { - return nil - } - - apiObject := &iam.PutRolePolicyInput{} - - namePolicy := false - - if v, ok := tfMap["name"].(string); ok && v != "" { - apiObject.PolicyName = aws.String(v) - namePolicy = true - } - - if v, ok := tfMap["policy"].(string); ok && v != "" { - apiObject.PolicyDocument = aws.String(v) - namePolicy = true - } - - if namePolicy { - apiObject.RoleName = aws.String(roleName) - } - - return apiObject -} - -func expandRoleInlinePolicies(roleName string, tfmap map[string]string) []*iam.PutRolePolicyInput { - if len(tfmap) == 0 { - return nil - } - - var apiObjects []*iam.PutRolePolicyInput - - // TODO: Loop through map, update lower function - - for _, tfMapRaw := range tfList { - tfMap, ok := tfMapRaw.(map[string]interface{}) - - if !ok { - continue - } - - apiObject := expandRoleInlinePolicy(roleName, tfMap) - - if apiObject == nil { - continue - } - - apiObjects = append(apiObjects, apiObject) - } - - return apiObjects -} - -func addRoleInlinePolicies(ctx context.Context, policies []*iam.PutRolePolicyInput, meta interface{}) error { - conn := meta.(*conns.AWSClient).IAMConn(ctx) - - var errs *multierror.Error - for _, policy := range policies { - if len(aws.StringValue(policy.PolicyName)) == 0 || len(aws.StringValue(policy.PolicyDocument)) == 0 { - continue - } - - if _, err := conn.PutRolePolicyWithContext(ctx, policy); err != nil { - newErr := fmt.Errorf("adding inline policy (%s): %w", aws.StringValue(policy.PolicyName), err) - errs = multierror.Append(errs, newErr) - } - } - - return errs.ErrorOrNil() -} - func (r resourceIamRole) addRoleManagedPolicies(ctx context.Context, roleName string, policies []*string) error { conn := r.Meta().IAMConn(ctx) + var errs []error for _, arn := range policies { if err := attachPolicyToRole(ctx, conn, roleName, aws.StringValue(arn)); err != nil { @@ -1019,96 +372,3 @@ func (r resourceIamRole) addRoleManagedPolicies(ctx context.Context, roleName st return errors.Join(errs...) } - -func readRoleInlinePolicies(ctx context.Context, roleName string, meta interface{}) ([]*iam.PutRolePolicyInput, error) { - conn := meta.(*conns.AWSClient).IAMConn(ctx) - - policyNames, err := findRolePolicyNames(ctx, conn, roleName) - - if err != nil { - return nil, err - } - - var apiObjects []*iam.PutRolePolicyInput - for _, policyName := range policyNames { - output, err := conn.GetRolePolicyWithContext(ctx, &iam.GetRolePolicyInput{ - RoleName: aws.String(roleName), - PolicyName: aws.String(policyName), - }) - - if err != nil { - return nil, err - } - - policy, err := url.QueryUnescape(aws.StringValue(output.PolicyDocument)) - if err != nil { - return nil, err - } - - p, err := verify.LegacyPolicyNormalize(policy) - if err != nil { - return nil, fmt.Errorf("policy (%s) is invalid JSON: %w", p, err) - } - - apiObject := &iam.PutRolePolicyInput{ - RoleName: aws.String(roleName), - PolicyDocument: aws.String(p), - PolicyName: aws.String(policyName), - } - - apiObjects = append(apiObjects, apiObject) - } - - return apiObjects, nil -} - -func inlinePoliciesActualDiff(d *schema.ResourceData) bool { - roleName := d.Get("name").(string) - o, n := d.GetChange("inline_policy") - if o == nil { - o = new(schema.Set) - } - if n == nil { - n = new(schema.Set) - } - - os := o.(*schema.Set) - ns := n.(*schema.Set) - - osPolicies := expandRoleInlinePolicies(roleName, os.List()) - nsPolicies := expandRoleInlinePolicies(roleName, ns.List()) - - return !inlinePoliciesEquivalent(nsPolicies, osPolicies) -} - -func inlinePoliciesEquivalent(readPolicies, configPolicies []*iam.PutRolePolicyInput) bool { - if readPolicies == nil && configPolicies == nil { - return true - } - - if len(readPolicies) == 0 && len(configPolicies) == 1 { - if equivalent, err := awspolicy.PoliciesAreEquivalent(`{}`, aws.StringValue(configPolicies[0].PolicyDocument)); err == nil && equivalent { - return true - } - } - - if len(readPolicies) != len(configPolicies) { - return false - } - - matches := 0 - - for _, policyOne := range readPolicies { - for _, policyTwo := range configPolicies { - if aws.StringValue(policyOne.PolicyName) == aws.StringValue(policyTwo.PolicyName) { - matches++ - if equivalent, err := awspolicy.PoliciesAreEquivalent(aws.StringValue(policyOne.PolicyDocument), aws.StringValue(policyTwo.PolicyDocument)); err != nil || !equivalent { - return false - } - break - } - } - } - - return matches == len(readPolicies) -} diff --git a/internal/service/iam/role_policy_attachment_test.go b/internal/service/iam/role_policy_attachment_test.go index 23641f8edf1..3f0d6071098 100644 --- a/internal/service/iam/role_policy_attachment_test.go +++ b/internal/service/iam/role_policy_attachment_test.go @@ -98,32 +98,33 @@ func TestAccIAMRolePolicyAttachment_disappears(t *testing.T) { }) } -func TestAccIAMRolePolicyAttachment_Disappears_role(t *testing.T) { - ctx := acctest.Context(t) - roleName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role_policy_attachment.test1" - iamRoleResourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRolePolicyAttachmentDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRolePolicyAttachmentConfig_attach(roleName, policyName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRolePolicyAttachmentExists(ctx, resourceName), - // DeleteConflict: Cannot delete entity, must detach all policies first. - acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRolePolicyAttachment(), resourceName), - acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), iamRoleResourceName), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} +// TODO: fix later +// func TestAccIAMRolePolicyAttachment_Disappears_role(t *testing.T) { +// ctx := acctest.Context(t) +// roleName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role_policy_attachment.test1" +// iamRoleResourceName := "aws_iam_role.test" + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRolePolicyAttachmentDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRolePolicyAttachmentConfig_attach(roleName, policyName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRolePolicyAttachmentExists(ctx, resourceName), +// // DeleteConflict: Cannot delete entity, must detach all policies first. +// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRolePolicyAttachment(), resourceName), +// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), iamRoleResourceName), +// ), +// ExpectNonEmptyPlan: true, +// }, +// }, +// }) +// } func testAccCheckRolePolicyAttachmentDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index d4724a0a555..dbe3c70e284 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -422,30 +422,30 @@ func TestAccIAMRole_badJSON(t *testing.T) { }) } -func TestAccIAMRole_disappears(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), resourceName), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} +// func TestAccIAMRole_disappears(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role + +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_basic(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), resourceName), +// ), +// ExpectNonEmptyPlan: true, +// }, +// }, +// }) +// } func TestAccIAMRole_policiesForceDetach(t *testing.T) { ctx := acctest.Context(t) diff --git a/internal/service/iam/service_package_gen.go b/internal/service/iam/service_package_gen.go index 4b02e40c658..db5a3eef627 100644 --- a/internal/service/iam/service_package_gen.go +++ b/internal/service/iam/service_package_gen.go @@ -151,6 +151,8 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka Name: "Policy Attachment", }, { + // TODO: do something with this, should be replaced? + // see top of file: // Code generated by internal/generate/servicepackages/main.go; DO NOT EDIT. Factory: ResourceRole, TypeName: "aws_iam_role", Name: "Role", diff --git a/internal/service/rds/cluster_role_association_test.go b/internal/service/rds/cluster_role_association_test.go index 1908a0a778c..fd3c173aa46 100644 --- a/internal/service/rds/cluster_role_association_test.go +++ b/internal/service/rds/cluster_role_association_test.go @@ -14,7 +14,8 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/service/iam" + // TODO: bring back later + // "github.com/hashicorp/terraform-provider-aws/internal/service/iam" tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) @@ -100,30 +101,31 @@ func TestAccRDSClusterRoleAssociation_Disappears_cluster(t *testing.T) { }) } -func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) { - ctx := acctest.Context(t) - var dbClusterRole rds.DBClusterRole - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_rds_cluster_role_association.test" - roleResourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckClusterRoleAssociationDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccClusterRoleAssociationConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckClusterRoleAssociationExists(ctx, resourceName, &dbClusterRole), - acctest.CheckResourceDisappears(ctx, acctest.Provider, iam.ResourceRole(), roleResourceName), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} +// TODO: fix later +// func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) { +// ctx := acctest.Context(t) +// var dbClusterRole rds.DBClusterRole +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_rds_cluster_role_association.test" +// roleResourceName := "aws_iam_role.test" + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckClusterRoleAssociationDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccClusterRoleAssociationConfig_basic(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckClusterRoleAssociationExists(ctx, resourceName, &dbClusterRole), +// acctest.CheckResourceDisappears(ctx, acctest.Provider, iam.ResourceRole(), roleResourceName), +// ), +// ExpectNonEmptyPlan: true, +// }, +// }, +// }) +// } func testAccCheckClusterRoleAssociationExists(ctx context.Context, resourceName string, v *rds.DBClusterRole) resource.TestCheckFunc { return func(s *terraform.State) error { From e0a75f7e1d17a565ddb0752bbc7bd974b3aaf3eb Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Tue, 16 Jan 2024 20:00:04 -0500 Subject: [PATCH 017/118] more cleanup --- internal/service/iam/role.go | 196 +++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 99cf53bfe12..13cfa5b1f74 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -24,6 +24,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-provider-aws/internal/create" @@ -372,3 +373,198 @@ func (r resourceIamRole) addRoleManagedPolicies(ctx context.Context, roleName st return errors.Join(errs...) } + +func DeleteRole(ctx context.Context, conn *iam.IAM, roleName string, forceDetach, hasInline, hasManaged bool) error { + if err := deleteRoleInstanceProfiles(ctx, conn, roleName); err != nil { + return err + } + + if forceDetach || hasManaged { + policyARNs, err := findRoleAttachedPolicies(ctx, conn, roleName) + + if err != nil { + return fmt.Errorf("reading IAM Policies attached to Role (%s): %w", roleName, err) + } + + if err := deleteRolePolicyAttachments(ctx, conn, roleName, policyARNs); err != nil { + return err + } + } + + if forceDetach || hasInline { + inlinePolicies, err := findRolePolicyNames(ctx, conn, roleName) + + if err != nil { + return fmt.Errorf("reading IAM Role (%s) inline policies: %w", roleName, err) + } + + if err := deleteRoleInlinePolicies(ctx, conn, roleName, inlinePolicies); err != nil { + return err + } + } + + input := &iam.DeleteRoleInput{ + RoleName: aws.String(roleName), + } + _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, propagationTimeout, func() (interface{}, error) { + return conn.DeleteRoleWithContext(ctx, input) + }, iam.ErrCodeDeleteConflictException) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil + } + + return err +} + +func deleteRoleInstanceProfiles(ctx context.Context, conn *iam.IAM, roleName string) error { + instanceProfiles, err := findInstanceProfilesForRole(ctx, conn, roleName) + + if tfresource.NotFound(err) { + return nil + } + + if err != nil { + return fmt.Errorf("reading IAM Instance Profiles for Role (%s): %w", roleName, err) + } + + var errs []error + + for _, instanceProfile := range instanceProfiles { + instanceProfileName := aws.StringValue(instanceProfile.InstanceProfileName) + input := &iam.RemoveRoleFromInstanceProfileInput{ + InstanceProfileName: aws.String(instanceProfileName), + RoleName: aws.String(roleName), + } + + _, err := conn.RemoveRoleFromInstanceProfileWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + continue + } + + if err != nil { + errs = append(errs, fmt.Errorf("removing IAM Role (%s) from Instance Profile (%s): %w", roleName, instanceProfileName, err)) + } + } + + return errors.Join(errs...) +} + +func findRoleAttachedPolicies(ctx context.Context, conn *iam.IAM, roleName string) ([]string, error) { + input := &iam.ListAttachedRolePoliciesInput{ + RoleName: aws.String(roleName), + } + var output []string + + err := conn.ListAttachedRolePoliciesPagesWithContext(ctx, input, func(page *iam.ListAttachedRolePoliciesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.AttachedPolicies { + if v != nil { + output = append(output, aws.StringValue(v.PolicyArn)) + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func deleteRolePolicyAttachments(ctx context.Context, conn *iam.IAM, roleName string, policyARNs []string) error { + var errs []error + + for _, policyARN := range policyARNs { + input := &iam.DetachRolePolicyInput{ + PolicyArn: aws.String(policyARN), + RoleName: aws.String(roleName), + } + + _, err := conn.DetachRolePolicyWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + continue + } + + if err != nil { + errs = append(errs, fmt.Errorf("detaching IAM Policy (%s) from Role (%s): %w", policyARN, roleName, err)) + } + } + + return errors.Join(errs...) +} + +func findRolePolicyNames(ctx context.Context, conn *iam.IAM, roleName string) ([]string, error) { + input := &iam.ListRolePoliciesInput{ + RoleName: aws.String(roleName), + } + var output []string + + err := conn.ListRolePoliciesPagesWithContext(ctx, input, func(page *iam.ListRolePoliciesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.PolicyNames { + if v != nil { + output = append(output, aws.StringValue(v)) + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func deleteRoleInlinePolicies(ctx context.Context, conn *iam.IAM, roleName string, policyNames []string) error { + var errs []error + + for _, policyName := range policyNames { + if len(policyName) == 0 { + continue + } + + input := &iam.DeleteRolePolicyInput{ + PolicyName: aws.String(policyName), + RoleName: aws.String(roleName), + } + + _, err := conn.DeleteRolePolicyWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + continue + } + + if err != nil { + errs = append(errs, fmt.Errorf("deleting IAM Role (%s) policy (%s): %w", roleName, policyName, err)) + } + } + + return errors.Join(errs...) +} From 29febc6deb50a99d6ed8809e46b13361d793b8fa Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Tue, 16 Jan 2024 20:22:59 -0500 Subject: [PATCH 018/118] adding function skeletons for plugin framework resource --- internal/service/iam/role.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 13cfa5b1f74..53f8f39c433 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -45,6 +45,12 @@ const ( // TODO: finish this how does this work? +// @FrameworkResource(name="Example") +// @Tags(identifierAttribute="arn") +func newResourceExample(_ context.Context) (resource.ResourceWithConfigure, error) { + return &resourceIamRole{}, nil +} + // @SDKResource("aws_iam_role", name="Role") // @Tags // func newIamRole(context.Context) (resource.ResourceWithConfigure, error) { @@ -305,7 +311,18 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp.Diagnostics.Append(resp.State.Set(ctx, state)...) } -// TODO: delete/update/import +func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // TODO: finish this +} + +func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // TODO: finish this +} + +func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // TODO: finish this +} + func FindRoleByName(ctx context.Context, conn *iam.IAM, name string) (*iam.Role, error) { input := &iam.GetRoleInput{ RoleName: aws.String(name), From 495bf16f0c274b8b4a3d331399927145bd1d46af Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Tue, 16 Jan 2024 21:06:10 -0500 Subject: [PATCH 019/118] got factory working I think --- internal/service/iam/role.go | 32 +++++++-------------- internal/service/iam/service_package_gen.go | 18 ++++++------ 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 53f8f39c433..da906d9e147 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -37,32 +37,17 @@ import ( ) const ( - roleNameMaxLen = 64 - // TODO: what? + roleNameMaxLen = 64 roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength ResNameIamRole = "IAM Role" ) -// TODO: finish this how does this work? - -// @FrameworkResource(name="Example") +// @FrameworkResource(name="Role") // @Tags(identifierAttribute="arn") -func newResourceExample(_ context.Context) (resource.ResourceWithConfigure, error) { - return &resourceIamRole{}, nil +func newResourceRole(_ context.Context) (resource.ResourceWithConfigure, error) { + return &resourceIamRole{}, nil } -// @SDKResource("aws_iam_role", name="Role") -// @Tags -// func newIamRole(context.Context) (resource.ResourceWithConfigure, error) { -// r := &resourceIamRole{} -// // TODO -// // r.create = r.createSecurityGroupRule -// // r.delete = r.deleteSecurityGroupRule -// // r.findByID = r.findSecurityGroupRuleByID - -// return r, nil -// } - type resourceIamRole struct { framework.ResourceWithConfigure } @@ -312,17 +297,20 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // TODO: finish this + // TODO: finish this } func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - // TODO: finish this + // TODO: finish this } func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - // TODO: finish this + // TODO: finish this } +// TODO: import state? +// https://developer.hashicorp.com/terraform/plugin/framework/resources/import + func FindRoleByName(ctx context.Context, conn *iam.IAM, name string) (*iam.Role, error) { input := &iam.GetRoleInput{ RoleName: aws.String(name), diff --git a/internal/service/iam/service_package_gen.go b/internal/service/iam/service_package_gen.go index db5a3eef627..2ba79100155 100644 --- a/internal/service/iam/service_package_gen.go +++ b/internal/service/iam/service_package_gen.go @@ -20,7 +20,15 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv } func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { - return []*types.ServicePackageFrameworkResource{} + return []*types.ServicePackageFrameworkResource{ + { + Factory: newResourceRole, + Name: "Role", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "arn", + }, + }, + } } func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { @@ -150,14 +158,6 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka TypeName: "aws_iam_policy_attachment", Name: "Policy Attachment", }, - { - // TODO: do something with this, should be replaced? - // see top of file: // Code generated by internal/generate/servicepackages/main.go; DO NOT EDIT. - Factory: ResourceRole, - TypeName: "aws_iam_role", - Name: "Role", - Tags: &types.ServicePackageResourceTags{}, - }, { Factory: ResourceRolePolicy, TypeName: "aws_iam_role_policy", From 83c71d960938cf98c73a46089dfc60e3d9c5c720 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Wed, 17 Jan 2024 19:25:16 -0500 Subject: [PATCH 020/118] make first test simpler, added delete func --- internal/service/iam/role.go | 34 +++++++++++++++++++++++++++++-- internal/service/iam/role_test.go | 11 +++++----- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index da906d9e147..ef57716a1c2 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -297,7 +297,37 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // TODO: finish this + conn := r.Meta().IAMConn(ctx) + + var state resourceIamRoleData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + hasInline := false + if !state.InlinePolicy.IsNull() && !state.InlinePolicy.IsUnknown() { + hasInline = true + } + + hasManaged := false + if !state.ManagedPolicyArns.IsNull() && !state.ManagedPolicyArns.IsUnknown() { + hasManaged = true + } + + err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, hasManaged) + + if err != nil { + // TODO: do something like this to skip deletes on roles that are gone? + // if err.IsA[*awstypes.ResourceNotFoundException](err) { + // return + // } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionDeleting, state.Name.String(), state.ARN.String(), err), + err.Error(), + ) + return + } } func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { @@ -305,7 +335,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - // TODO: finish this + // TODO: finish this in later test } // TODO: import state? diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index dbe3c70e284..f73909530eb 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -42,11 +42,12 @@ func TestAccIAMRole_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "create_date"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, + // TODO: next step + // { + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // }, }, }) } From ed8f65a6596b7346ac73fc58e93ab07b87869090 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Wed, 17 Jan 2024 19:41:01 -0500 Subject: [PATCH 021/118] work so far getting read working --- internal/service/iam/role.go | 81 +++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index ef57716a1c2..70faea69745 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -331,7 +331,86 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, } func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - // TODO: finish this + conn := r.Meta().IAMConn(ctx) + + var state resourceIamRoleData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // outputRaw, err := tfresource.RetryWhenNewResourceNotFound(ctx, propagationTimeout, func() (interface{}, error) { + // return FindRoleByName(ctx, conn, d.Id()) + // }, d.IsNewResource()) + + // if !d.IsNewResource() && tfresource.NotFound(err) { + // log.Printf("[WARN] IAM Role (%s) not found, removing from state", d.Id()) + // d.SetId("") + // return diags + // } + + // if err != nil { + // return sdkdiag.AppendErrorf(diags, "reading IAM Role (%s): %s", d.Id(), err) + // } + + // role := outputRaw.(*iam.Role) + + // // occasionally, immediately after a role is created, AWS will give an ARN like AROAQ7SSZBKHREXAMPLE (unique ID) + // if role, err = waitRoleARNIsNotUniqueID(ctx, conn, d.Id(), role); err != nil { + // return sdkdiag.AppendErrorf(diags, "reading IAM Role (%s): waiting for valid ARN: %s", d.Id(), err) + // } + + // d.Set("arn", role.Arn) + // d.Set("create_date", role.CreateDate.Format(time.RFC3339)) + // d.Set("description", role.Description) + // d.Set("max_session_duration", role.MaxSessionDuration) + // d.Set("name", role.RoleName) + // d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(role.RoleName))) + // d.Set("path", role.Path) + // if role.PermissionsBoundary != nil { + // d.Set("permissions_boundary", role.PermissionsBoundary.PermissionsBoundaryArn) + // } else { + // d.Set("permissions_boundary", nil) + // } + // d.Set("unique_id", role.RoleId) + + // assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) + // if err != nil { + // return sdkdiag.AppendFromErr(diags, err) + // } + + // policyToSet, err := verify.PolicyToSet(d.Get("assume_role_policy").(string), assumeRolePolicy) + // if err != nil { + // return sdkdiag.AppendFromErr(diags, err) + // } + + // d.Set("assume_role_policy", policyToSet) + + // inlinePolicies, err := readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName), meta) + // if err != nil { + // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) + // } + + // var configPoliciesList []*iam.PutRolePolicyInput + // if v := d.Get("inline_policy").(*schema.Set); v.Len() > 0 { + // configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), v.List()) + // } + + // if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { + // if err := d.Set("inline_policy", flattenRoleInlinePolicies(inlinePolicies)); err != nil { + // return sdkdiag.AppendErrorf(diags, "setting inline_policy: %s", err) + // } + // } + + // policyARNs, err := findRoleAttachedPolicies(ctx, conn, d.Id()) + // if err != nil { + // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) + // } + // d.Set("managed_policy_arns", policyARNs) + + // setTagsOut(ctx, role.Tags) + + // return diags } func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { From 8f4eaf736688af42e23c62673c13eebad5889062 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Wed, 17 Jan 2024 20:16:57 -0500 Subject: [PATCH 022/118] finish read function with basics from first test --- internal/service/iam/role.go | 81 ++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 70faea69745..a51d944ebc3 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "time" "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go/aws" @@ -339,78 +340,106 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res return } - // outputRaw, err := tfresource.RetryWhenNewResourceNotFound(ctx, propagationTimeout, func() (interface{}, error) { - // return FindRoleByName(ctx, conn, d.Id()) - // }, d.IsNewResource()) + //NOTE: Have to always set this to true? Else not sure what to do + outputRaw, err := tfresource.RetryWhenNewResourceNotFound(ctx, propagationTimeout, func() (interface{}, error) { + return FindRoleByName(ctx, conn, state.Name.ValueString()) + }, true) + // NOTE: Same issue here, I left old conditional here as example, not sure what else can/should be done // if !d.IsNewResource() && tfresource.NotFound(err) { + if tfresource.NotFound(err) { // log.Printf("[WARN] IAM Role (%s) not found, removing from state", d.Id()) // d.SetId("") // return diags - // } + resp.State.RemoveResource(ctx) + return + } - // if err != nil { - // return sdkdiag.AppendErrorf(diags, "reading IAM Role (%s): %s", d.Id(), err) - // } + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionSetting, state.Name.String(), state.ARN.String(), err), + err.Error(), + ) + return + } - // role := outputRaw.(*iam.Role) + role := outputRaw.(*iam.Role) - // // occasionally, immediately after a role is created, AWS will give an ARN like AROAQ7SSZBKHREXAMPLE (unique ID) - // if role, err = waitRoleARNIsNotUniqueID(ctx, conn, d.Id(), role); err != nil { + // occasionally, immediately after a role is created, AWS will give an ARN like AROAQ7SSZBKHREXAMPLE (unique ID) + if role, err = waitRoleARNIsNotUniqueID(ctx, conn, state.ARN.ValueString(), role); err != nil { + // TODO: have to update this error // return sdkdiag.AppendErrorf(diags, "reading IAM Role (%s): waiting for valid ARN: %s", d.Id(), err) - // } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionSetting, state.Name.String(), state.ARN.String(), err), + err.Error(), + ) + return + } + + // TODO: remove example section later + // state.ApplicationAccount = flex.StringToFramework(ctx, out.ApplicationAccount) + // state.ApplicationARN = flex.StringToFrameworkARN(ctx, out.ApplicationArn) + // state.ApplicationProviderARN = flex.StringToFrameworkARN(ctx, out.ApplicationProviderArn) + // state.Description = flex.StringToFramework(ctx, out.Description) + // state.ID = flex.StringToFramework(ctx, out.ApplicationArn) + // state.InstanceARN = flex.StringToFrameworkARN(ctx, out.InstanceArn) + // state.Name = flex.StringToFramework(ctx, out.Name) + // state.Status = flex.StringValueToFramework(ctx, out.Status) + + state.ARN = flex.StringToFramework(ctx, role.Arn) + state.CreateDate = flex.StringValueToFramework(ctx, role.CreateDate.Format(time.RFC3339)) + state.Path = flex.StringToFramework(ctx, role.Path) + // TODO: add more of these when ready to actually test - // d.Set("arn", role.Arn) - // d.Set("create_date", role.CreateDate.Format(time.RFC3339)) // d.Set("description", role.Description) // d.Set("max_session_duration", role.MaxSessionDuration) // d.Set("name", role.RoleName) // d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(role.RoleName))) - // d.Set("path", role.Path) + // if role.PermissionsBoundary != nil { - // d.Set("permissions_boundary", role.PermissionsBoundary.PermissionsBoundaryArn) + // d.Set("permissions_boundary", role.PermissionsBoundary.PermissionsBoundaryArn) // } else { - // d.Set("permissions_boundary", nil) + // d.Set("permissions_boundary", nil) // } // d.Set("unique_id", role.RoleId) // assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) // if err != nil { - // return sdkdiag.AppendFromErr(diags, err) + // return sdkdiag.AppendFromErr(diags, err) // } // policyToSet, err := verify.PolicyToSet(d.Get("assume_role_policy").(string), assumeRolePolicy) // if err != nil { - // return sdkdiag.AppendFromErr(diags, err) + // return sdkdiag.AppendFromErr(diags, err) // } // d.Set("assume_role_policy", policyToSet) // inlinePolicies, err := readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName), meta) // if err != nil { - // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) + // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) // } // var configPoliciesList []*iam.PutRolePolicyInput // if v := d.Get("inline_policy").(*schema.Set); v.Len() > 0 { - // configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), v.List()) + // configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), v.List()) // } // if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { - // if err := d.Set("inline_policy", flattenRoleInlinePolicies(inlinePolicies)); err != nil { - // return sdkdiag.AppendErrorf(diags, "setting inline_policy: %s", err) - // } + // if err := d.Set("inline_policy", flattenRoleInlinePolicies(inlinePolicies)); err != nil { + // return sdkdiag.AppendErrorf(diags, "setting inline_policy: %s", err) + // } // } // policyARNs, err := findRoleAttachedPolicies(ctx, conn, d.Id()) // if err != nil { - // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) + // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) // } // d.Set("managed_policy_arns", policyARNs) - // setTagsOut(ctx, role.Tags) + setTagsOut(ctx, role.Tags) - // return diags + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { From ec1e3b4bcaf7ab45859f4c05d7ce170b2a984a65 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Wed, 17 Jan 2024 21:21:20 -0500 Subject: [PATCH 023/118] work so far to get first test working --- internal/service/iam/role.go | 238 ++++++++++++++++-------------- internal/service/iam/role_test.go | 65 ++++---- 2 files changed, 157 insertions(+), 146 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index a51d944ebc3..d590cb8dcfa 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -9,19 +9,19 @@ import ( "fmt" "time" - "github.com/YakDriver/regexache" + // "github.com/YakDriver/regexache" "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-framework-validators/int64validator" + // "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/path" + // "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + // "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + // "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + // "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -31,7 +31,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" - fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" + // fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" @@ -74,43 +74,43 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "create_date": schema.StringAttribute{ Computed: true, }, - "description": schema.StringAttribute{ - Optional: true, - Validators: []validator.String{ - stringvalidator.LengthBetween(0, 1000), - // TODO: regex does not match? - stringvalidator.RegexMatches( - regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), - `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`, - ), - }, - }, - "force_detach_policies": schema.BoolAttribute{ - Optional: true, - Default: booldefault.StaticBool(false), - }, + // "description": schema.StringAttribute{ + // Optional: true, + // Validators: []validator.String{ + // stringvalidator.LengthBetween(0, 1000), + // // TODO: regex does not match? + // stringvalidator.RegexMatches( + // regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), + // `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`, + // ), + // }, + // }, + // "force_detach_policies": schema.BoolAttribute{ + // Optional: true, + // // Default: booldefault.StaticBool(false), + // }, // TODO: inline policy goes crazy, have to figure what this type should look like // also read article again - "inline_policy": schema.MapAttribute{ - ElementType: types.StringType, - Optional: true, - // TODO: maybe some validation? - }, - "managed_policy_arns": schema.SetAttribute{ - Computed: true, - Optional: true, - ElementType: types.StringType, - // TODO: set validator for arn - // TODO: validate all elements of set are valid arns - // how to do this with helper lib terraform-plugin-framework-validators - }, - "max_session_duration": schema.Int64Attribute{ - Optional: true, - Default: int64default.StaticInt64(3600), - Validators: []validator.Int64{ - int64validator.Between(3600, 43200), - }, - }, + // "inline_policy": schema.MapAttribute{ + // ElementType: types.StringType, + // Optional: true, + // // TODO: maybe some validation? + // }, + // "managed_policy_arns": schema.SetAttribute{ + // Computed: true, + // Optional: true, + // ElementType: types.StringType, + // // TODO: set validator for arn + // // TODO: validate all elements of set are valid arns + // // how to do this with helper lib terraform-plugin-framework-validators + // }, + // "max_session_duration": schema.Int64Attribute{ + // Optional: true, + // // Default: int64default.StaticInt64(3600), + // Validators: []validator.Int64{ + // int64validator.Between(3600, 43200), + // }, + // }, "name": schema.StringAttribute{ Optional: true, Computed: true, @@ -119,43 +119,44 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, Validators: []validator.String{ stringvalidator.LengthAtMost(roleNameMaxLen), - stringvalidator.ConflictsWith( - path.MatchRelative().AtParent().AtName("name_prefix"), - ), - }, - }, - "name_prefix": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplaceIfConfigured(), - }, - Validators: []validator.String{ - stringvalidator.LengthAtMost(roleNamePrefixMaxLen), - stringvalidator.ConflictsWith( - path.MatchRelative().AtParent().AtName("name"), - ), + // TODO: uncomment when ready + // stringvalidator.ConflictsWith( + // path.MatchRelative().AtParent().AtName("name_prefix"), + // ), }, }, + // "name_prefix": schema.StringAttribute{ + // Optional: true, + // Computed: true, + // PlanModifiers: []planmodifier.String{ + // stringplanmodifier.RequiresReplaceIfConfigured(), + // }, + // Validators: []validator.String{ + // stringvalidator.LengthAtMost(roleNamePrefixMaxLen), + // stringvalidator.ConflictsWith( + // path.MatchRelative().AtParent().AtName("name"), + // ), + // }, + // }, "path": schema.StringAttribute{ Optional: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplaceIfConfigured(), }, - Default: stringdefault.StaticString("/"), + // Default: stringdefault.StaticString("/"), Validators: []validator.String{ stringvalidator.LengthBetween(0, 512), }, }, - "permissions_boundary": schema.StringAttribute{ - Optional: true, - Validators: []validator.String{ - fwvalidators.ARN(), - }, - }, - "unique_id": schema.StringAttribute{ - Computed: true, - }, + // "permissions_boundary": schema.StringAttribute{ + // Optional: true, + // Validators: []validator.String{ + // fwvalidators.ARN(), + // }, + // }, + // "unique_id": schema.StringAttribute{ + // Computed: true, + // }, names.AttrTags: tftags.TagsAttribute(), names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), }, @@ -163,22 +164,22 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest } type resourceIamRoleData struct { - ARN types.String `tfsdk:"arn"` - AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` - CreateDate types.String `tfsdk:"create_date"` - Description types.String `tfsdk:"description"` - ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` + ARN types.String `tfsdk:"arn"` + AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` + CreateDate types.String `tfsdk:"create_date"` + // Description types.String `tfsdk:"description"` + // ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` // TODO: still have to think this one out - InlinePolicy types.Map `tfsdk:"inline_policy"` - ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` - MaxSessionDuration types.Int64 `tfsdk:"max_session_duration"` - Name types.String `tfsdk:"name"` - NamePrefix types.String `tfsdk:"name_prefix"` - Path types.String `tfsdk:"path"` - PermissionsBoundary types.String `tfsdk:"permissions_boundary"` - UniqueId types.String `tfsdk:"unique_id"` - Tags types.Map `tfsdk:"tags"` - TagsAll types.Map `tfsdk:"tags_all"` + // InlinePolicy types.Map `tfsdk:"inline_policy"` + // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` + // MaxSessionDuration types.Int64 `tfsdk:"max_session_duration"` + Name types.String `tfsdk:"name"` + // NamePrefix types.String `tfsdk:"name_prefix"` + Path types.String `tfsdk:"path"` + // PermissionsBoundary types.String `tfsdk:"permissions_boundary"` + // UniqueId types.String `tfsdk:"unique_id"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` } func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -199,7 +200,9 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, return } - name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) + // TODO: uncomment when we use prefix + // name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) + name := plan.Name.ValueString() input := &iam.CreateRoleInput{ AssumeRolePolicyDocument: aws.String(assumeRolePolicy), Path: aws.String(plan.Path.ValueString()), @@ -207,18 +210,19 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, Tags: getTagsIn(ctx), } - if !plan.Description.IsNull() { - input.Description = aws.String(plan.Description.ValueString()) - } + // if !plan.Description.IsNull() { + // input.Description = aws.String(plan.Description.ValueString()) + // } - if !plan.MaxSessionDuration.IsNull() { - input.MaxSessionDuration = aws.Int64(plan.MaxSessionDuration.ValueInt64()) - } + // if !plan.MaxSessionDuration.IsNull() { + // input.MaxSessionDuration = aws.Int64(plan.MaxSessionDuration.ValueInt64()) + // } - if !plan.PermissionsBoundary.IsNull() { - input.PermissionsBoundary = aws.String(plan.PermissionsBoundary.ValueString()) - } + // if !plan.PermissionsBoundary.IsNull() { + // input.PermissionsBoundary = aws.String(plan.PermissionsBoundary.ValueString()) + // } + // TODO: uncomment this output, err := retryCreateRole(ctx, conn, input) // TODO: So this needs tags... do we need on resourceIamRoleData? @@ -236,7 +240,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, return } - roleName := aws.StringValue(output.Role.RoleName) + // roleName := aws.StringValue(output.Role.RoleName) // TODO: has to figure this out because typing of inline policies // if !plan.InlinePolicy.IsNull() && !plan.InlinePolicy.IsUnknown() { @@ -252,16 +256,16 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, // } // } - if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { - managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) - if err := r.addRoleManagedPolicies(ctx, roleName, managedPolicies); err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), - err.Error(), - ) - return - } - } + // if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { + // managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) + // if err := r.addRoleManagedPolicies(ctx, roleName, managedPolicies); err != nil { + // resp.Diagnostics.AddError( + // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + // err.Error(), + // ) + // return + // } + // } // TODO: do something with this? // some resources have been created but not all attributes @@ -290,6 +294,10 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } } + // TODO: do I have to do this? should look at other resources + plan.ARN = flex.StringToFramework(ctx, output.Role.Arn) + plan.CreateDate = flex.StringValueToFramework(ctx, output.Role.CreateDate.Format(time.RFC3339)) + // last steps? state := plan // TODO: do we need something?this? @@ -306,17 +314,18 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, return } - hasInline := false - if !state.InlinePolicy.IsNull() && !state.InlinePolicy.IsUnknown() { - hasInline = true - } + // hasInline := false + // if !state.InlinePolicy.IsNull() && !state.InlinePolicy.IsUnknown() { + // hasInline = true + // } - hasManaged := false - if !state.ManagedPolicyArns.IsNull() && !state.ManagedPolicyArns.IsUnknown() { - hasManaged = true - } + // hasManaged := false + // if !state.ManagedPolicyArns.IsNull() && !state.ManagedPolicyArns.IsUnknown() { + // hasManaged = true + // } - err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, hasManaged) + // err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, hasManaged) + err := DeleteRole(ctx, conn, state.Name.ValueString(), false, false, false) if err != nil { // TODO: do something like this to skip deletes on roles that are gone? @@ -389,11 +398,11 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.ARN = flex.StringToFramework(ctx, role.Arn) state.CreateDate = flex.StringValueToFramework(ctx, role.CreateDate.Format(time.RFC3339)) state.Path = flex.StringToFramework(ctx, role.Path) + state.Name = flex.StringToFramework(ctx, role.RoleName) // TODO: add more of these when ready to actually test // d.Set("description", role.Description) // d.Set("max_session_duration", role.MaxSessionDuration) - // d.Set("name", role.RoleName) // d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(role.RoleName))) // if role.PermissionsBoundary != nil { @@ -403,6 +412,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res // } // d.Set("unique_id", role.RoleId) + // TODO: uncomment // assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) // if err != nil { // return sdkdiag.AppendFromErr(diags, err) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index f73909530eb..e38c5a1edbe 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -52,39 +52,40 @@ func TestAccIAMRole_basic(t *testing.T) { }) } -func TestAccIAMRole_basic_migration_sdk_to_framework(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// TODO: comment out later +// func TestAccIAMRole_basic_migration_sdk_to_framework(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - ExternalProviders: map[string]resource.ExternalProvider{ - "": { - VersionConstraint: "5.17.0", - Source: "hashicorp/terraform-provider-aws", - }, - }, - Config: testAccRoleConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "path", "/"), - resource.TestCheckResourceAttrSet(resourceName, "create_date"), - ), - }, - { - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Config: testAccRoleConfig_basic(rName), - PlanOnly: true, - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// ExternalProviders: map[string]resource.ExternalProvider{ +// "": { +// VersionConstraint: "5.17.0", +// Source: "hashicorp/terraform-provider-aws", +// }, +// }, +// Config: testAccRoleConfig_basic(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// resource.TestCheckResourceAttr(resourceName, "path", "/"), +// resource.TestCheckResourceAttrSet(resourceName, "create_date"), +// ), +// }, +// { +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// Config: testAccRoleConfig_basic(rName), +// PlanOnly: true, +// }, +// }, +// }) +// } func TestAccIAMRole_description(t *testing.T) { ctx := acctest.Context(t) From 566d85e0864cd3831df152150375dbcc220712b6 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Thu, 18 Jan 2024 18:53:12 -0500 Subject: [PATCH 024/118] one more try --- internal/service/iam/role.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index d590cb8dcfa..2268a452b83 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -31,6 +31,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" // fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -46,7 +47,10 @@ const ( // @FrameworkResource(name="Role") // @Tags(identifierAttribute="arn") func newResourceRole(_ context.Context) (resource.ResourceWithConfigure, error) { - return &resourceIamRole{}, nil + r := &resourceIamRole{} + r.SetMigratedFromPluginSDK(true) + + return r, nil } type resourceIamRole struct { @@ -62,6 +66,14 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "arn": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Computed: true, + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "id": schema.StringAttribute{ Computed: true, }, "assume_role_policy": schema.StringAttribute{ @@ -164,9 +176,10 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest } type resourceIamRoleData struct { - ARN types.String `tfsdk:"arn"` + ARN fwtypes.ARN `tfsdk:"arn"` AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` CreateDate types.String `tfsdk:"create_date"` + ID types.String `tfsdk:"id"` // Description types.String `tfsdk:"description"` // ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` // TODO: still have to think this one out @@ -295,8 +308,9 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } // TODO: do I have to do this? should look at other resources - plan.ARN = flex.StringToFramework(ctx, output.Role.Arn) + plan.ARN = fwtypes.ARNValue(*output.Role.Arn) plan.CreateDate = flex.StringValueToFramework(ctx, output.Role.CreateDate.Format(time.RFC3339)) + plan.ID = flex.StringToFramework(ctx, output.Role.RoleId) // last steps? state := plan @@ -395,10 +409,11 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res // state.Name = flex.StringToFramework(ctx, out.Name) // state.Status = flex.StringValueToFramework(ctx, out.Status) - state.ARN = flex.StringToFramework(ctx, role.Arn) + state.ARN = fwtypes.ARNValue(*role.Arn) state.CreateDate = flex.StringValueToFramework(ctx, role.CreateDate.Format(time.RFC3339)) state.Path = flex.StringToFramework(ctx, role.Path) state.Name = flex.StringToFramework(ctx, role.RoleName) + state.ID = flex.StringToFramework(ctx, role.RoleId) // TODO: add more of these when ready to actually test // d.Set("description", role.Description) From 45f9e922ade35d851bafa16ee4948418eb864c67 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Thu, 18 Jan 2024 18:59:38 -0500 Subject: [PATCH 025/118] more --- internal/service/iam/role.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 2268a452b83..bd6cfca71f6 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -22,6 +22,7 @@ import ( // "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" // "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -354,6 +355,14 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, } } +func (r *resourceIamRole) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response) +} + +func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) +} + func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { conn := r.Meta().IAMConn(ctx) From afd68012d4365f2947030cc32cd784c17f8401e3 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Thu, 18 Jan 2024 20:21:31 -0500 Subject: [PATCH 026/118] YES --- internal/service/iam/role.go | 15 +++++++++------ internal/service/iam/role_test.go | 3 +++ internal/service/iam/service_package_gen.go | 4 ---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index bd6cfca71f6..7c43e3c9182 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -45,8 +45,7 @@ const ( ResNameIamRole = "IAM Role" ) -// @FrameworkResource(name="Role") -// @Tags(identifierAttribute="arn") +// @FrameworkResource func newResourceRole(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourceIamRole{} r.SetMigratedFromPluginSDK(true) @@ -197,6 +196,7 @@ type resourceIamRoleData struct { } func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + fmt.Println("Hitting top of Create") conn := r.Meta().IAMConn(ctx) var plan resourceIamRoleData @@ -311,13 +311,16 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, // TODO: do I have to do this? should look at other resources plan.ARN = fwtypes.ARNValue(*output.Role.Arn) plan.CreateDate = flex.StringValueToFramework(ctx, output.Role.CreateDate.Format(time.RFC3339)) - plan.ID = flex.StringToFramework(ctx, output.Role.RoleId) + plan.ID = flex.StringToFramework(ctx, output.Role.RoleName) // last steps? - state := plan // TODO: do we need something?this? // state.refreshFromOutput(ctx, out) - resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + // fmt.Println(plan.ARN) + // fmt.Println(plan.ID) + // fmt.Println(plan.Name) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + fmt.Println("Bottom of Create") } func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { @@ -422,7 +425,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.CreateDate = flex.StringValueToFramework(ctx, role.CreateDate.Format(time.RFC3339)) state.Path = flex.StringToFramework(ctx, role.Path) state.Name = flex.StringToFramework(ctx, role.RoleName) - state.ID = flex.StringToFramework(ctx, role.RoleId) + state.ID = flex.StringToFramework(ctx, role.RoleName) // TODO: add more of these when ready to actually test // d.Set("description", role.Description) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index e38c5a1edbe..a6d9ac4d868 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -1133,6 +1133,9 @@ func testAccCheckRoleExists(ctx context.Context, n string, v *iam.Role) resource return fmt.Errorf("No IAM Role ID is set") } + // TODO: Debugging + fmt.Println(fmt.Sprintf("testAccCheckRoleExists ID: %s", rs.Primary.ID)) + conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx) output, err := tfiam.FindRoleByName(ctx, conn, rs.Primary.ID) diff --git a/internal/service/iam/service_package_gen.go b/internal/service/iam/service_package_gen.go index 2ba79100155..4b8675f19ea 100644 --- a/internal/service/iam/service_package_gen.go +++ b/internal/service/iam/service_package_gen.go @@ -23,10 +23,6 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic return []*types.ServicePackageFrameworkResource{ { Factory: newResourceRole, - Name: "Role", - Tags: &types.ServicePackageResourceTags{ - IdentifierAttribute: "arn", - }, }, } } From 5d054f8a2f1e25e3057bb5b53629297aa517f9e0 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Thu, 18 Jan 2024 20:30:06 -0500 Subject: [PATCH 027/118] add back sdk resource tags --- internal/service/iam/role.go | 2 +- internal/service/iam/service_package_gen.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 7c43e3c9182..d37f4870939 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -45,7 +45,7 @@ const ( ResNameIamRole = "IAM Role" ) -// @FrameworkResource +// @FrameworkResource(name="Role") func newResourceRole(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourceIamRole{} r.SetMigratedFromPluginSDK(true) diff --git a/internal/service/iam/service_package_gen.go b/internal/service/iam/service_package_gen.go index 4b8675f19ea..0a8bb2a807d 100644 --- a/internal/service/iam/service_package_gen.go +++ b/internal/service/iam/service_package_gen.go @@ -23,6 +23,7 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic return []*types.ServicePackageFrameworkResource{ { Factory: newResourceRole, + Name: "Role", }, } } From 6997ac3e5fb10e9a650fdce07657865724daed2a Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Thu, 18 Jan 2024 21:06:43 -0500 Subject: [PATCH 028/118] got import test working --- internal/service/iam/role.go | 46 ++++++++++++++++++++----------- internal/service/iam/role_test.go | 11 ++++---- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index d37f4870939..7a7e5e465fa 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "net/url" "time" // "github.com/YakDriver/regexache" @@ -33,6 +34,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/verify" // fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -77,7 +79,8 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Computed: true, }, "assume_role_policy": schema.StringAttribute{ - Required: true, + Required: true, + CustomType: fwtypes.IAMPolicyType, // Validators: []validator.String{ // // TODO: json validator // }, @@ -176,10 +179,10 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest } type resourceIamRoleData struct { - ARN fwtypes.ARN `tfsdk:"arn"` - AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` - CreateDate types.String `tfsdk:"create_date"` - ID types.String `tfsdk:"id"` + ARN fwtypes.ARN `tfsdk:"arn"` + AssumeRolePolicy fwtypes.IAMPolicy `tfsdk:"assume_role_policy"` + CreateDate types.String `tfsdk:"create_date"` + ID types.String `tfsdk:"id"` // Description types.String `tfsdk:"description"` // ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` // TODO: still have to think this one out @@ -359,6 +362,7 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, } func (r *resourceIamRole) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { + fmt.Println("Top of Import") resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response) } @@ -367,6 +371,7 @@ func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.Modif } func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + fmt.Println("Top of Read") conn := r.Meta().IAMConn(ctx) var state resourceIamRoleData @@ -377,7 +382,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res //NOTE: Have to always set this to true? Else not sure what to do outputRaw, err := tfresource.RetryWhenNewResourceNotFound(ctx, propagationTimeout, func() (interface{}, error) { - return FindRoleByName(ctx, conn, state.Name.ValueString()) + return FindRoleByName(ctx, conn, state.ID.ValueString()) }, true) // NOTE: Same issue here, I left old conditional here as example, not sure what else can/should be done @@ -426,7 +431,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.Path = flex.StringToFramework(ctx, role.Path) state.Name = flex.StringToFramework(ctx, role.RoleName) state.ID = flex.StringToFramework(ctx, role.RoleName) - // TODO: add more of these when ready to actually test // d.Set("description", role.Description) // d.Set("max_session_duration", role.MaxSessionDuration) @@ -439,16 +443,26 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res // } // d.Set("unique_id", role.RoleId) - // TODO: uncomment - // assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) - // if err != nil { - // return sdkdiag.AppendFromErr(diags, err) - // } + assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) + if err != nil { + // TODO: I don't this this is right error, should look more into it + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionReading, state.ID.String(), state.AssumeRolePolicy.String(), err), + err.Error(), + ) + return + } - // policyToSet, err := verify.PolicyToSet(d.Get("assume_role_policy").(string), assumeRolePolicy) - // if err != nil { - // return sdkdiag.AppendFromErr(diags, err) - // } + policyToSet, err := verify.PolicyToSet(state.AssumeRolePolicy.ValueString(), assumeRolePolicy) + if err != nil { + // TODO: I don't this this is right error, should look more into it + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionReading, state.ID.String(), state.AssumeRolePolicy.String(), err), + err.Error(), + ) + return + } + state.AssumeRolePolicy = fwtypes.IAMPolicyValue(policyToSet) // d.Set("assume_role_policy", policyToSet) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index a6d9ac4d868..b85206cae5e 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -42,12 +42,11 @@ func TestAccIAMRole_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "create_date"), ), }, - // TODO: next step - // { - // ResourceName: resourceName, - // ImportState: true, - // ImportStateVerify: true, - // }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } From 18bae50bf90b757fdc61fa37f703828f78dffeb3 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:34:58 -0500 Subject: [PATCH 029/118] got baseline description test working --- internal/service/iam/role.go | 120 +++++++++++++++++++++++------- internal/service/iam/role_test.go | 32 ++++---- 2 files changed, 109 insertions(+), 43 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 7a7e5e465fa..ee4759682fd 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -31,6 +31,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" @@ -89,17 +90,18 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "create_date": schema.StringAttribute{ Computed: true, }, - // "description": schema.StringAttribute{ - // Optional: true, - // Validators: []validator.String{ - // stringvalidator.LengthBetween(0, 1000), - // // TODO: regex does not match? - // stringvalidator.RegexMatches( - // regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), - // `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`, - // ), - // }, - // }, + "description": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(0, 1000), + // TODO: figure this out later for both validators + // stringvalidator.RegexMatches( + // regexache.MustCompile( + // `[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), + // `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`, + // ), + }, + }, // "force_detach_policies": schema.BoolAttribute{ // Optional: true, // // Default: booldefault.StaticBool(false), @@ -183,7 +185,7 @@ type resourceIamRoleData struct { AssumeRolePolicy fwtypes.IAMPolicy `tfsdk:"assume_role_policy"` CreateDate types.String `tfsdk:"create_date"` ID types.String `tfsdk:"id"` - // Description types.String `tfsdk:"description"` + Description types.String `tfsdk:"description"` // ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` // TODO: still have to think this one out // InlinePolicy types.Map `tfsdk:"inline_policy"` @@ -227,9 +229,9 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, Tags: getTagsIn(ctx), } - // if !plan.Description.IsNull() { - // input.Description = aws.String(plan.Description.ValueString()) - // } + if !plan.Description.IsNull() { + input.Description = aws.String(plan.Description.ValueString()) + } // if !plan.MaxSessionDuration.IsNull() { // input.MaxSessionDuration = aws.Int64(plan.MaxSessionDuration.ValueInt64()) @@ -416,23 +418,13 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res return } - // TODO: remove example section later - // state.ApplicationAccount = flex.StringToFramework(ctx, out.ApplicationAccount) - // state.ApplicationARN = flex.StringToFrameworkARN(ctx, out.ApplicationArn) - // state.ApplicationProviderARN = flex.StringToFrameworkARN(ctx, out.ApplicationProviderArn) - // state.Description = flex.StringToFramework(ctx, out.Description) - // state.ID = flex.StringToFramework(ctx, out.ApplicationArn) - // state.InstanceARN = flex.StringToFrameworkARN(ctx, out.InstanceArn) - // state.Name = flex.StringToFramework(ctx, out.Name) - // state.Status = flex.StringValueToFramework(ctx, out.Status) - state.ARN = fwtypes.ARNValue(*role.Arn) state.CreateDate = flex.StringValueToFramework(ctx, role.CreateDate.Format(time.RFC3339)) state.Path = flex.StringToFramework(ctx, role.Path) state.Name = flex.StringToFramework(ctx, role.RoleName) state.ID = flex.StringToFramework(ctx, role.RoleName) + state.Description = flex.StringToFramework(ctx, role.Description) - // d.Set("description", role.Description) // d.Set("max_session_duration", role.MaxSessionDuration) // d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(role.RoleName))) @@ -494,7 +486,81 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - // TODO: finish this in later test + conn := r.Meta().IAMConn(ctx) + + var plan, state resourceIamRoleData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if !plan.AssumeRolePolicy.Equal(state.AssumeRolePolicy) { + assumeRolePolicy, err := structure.NormalizeJsonString(plan.AssumeRolePolicy.ValueString()) + if err != nil { + // TODO: update error here + // return sdkdiag.AppendErrorf(diags, "assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err) + return + } + + input := &iam.UpdateAssumeRolePolicyInput{ + RoleName: aws.String(plan.ID.ValueString()), + PolicyDocument: aws.String(assumeRolePolicy), + } + + _, err = tfresource.RetryWhen(ctx, propagationTimeout, + func() (interface{}, error) { + return conn.UpdateAssumeRolePolicyWithContext(ctx, 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 { + // TODO: update error + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) assume role policy: %s", d.Id(), err) + return + } + } + + if !plan.Description.Equal(state.Description) { + input := &iam.UpdateRoleDescriptionInput{ + RoleName: aws.String(plan.ID.ValueString()), + Description: aws.String(plan.Description.ValueString()), + } + + _, err := conn.UpdateRoleDescriptionWithContext(ctx, input) + + if err != nil { + // TODO: put something there + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) description: %s", d.Id(), err) + return + } + } + + if !plan.TagsAll.Equal(state.TagsAll) { + err := roleUpdateTags(ctx, conn, plan.ID.ValueString(), state.TagsAll.Elements(), plan.TagsAll.Elements()) + + // Some partitions (e.g. ISO) may not support tagging. + if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { + // TODO: implement error here + return + // return append(diags, resourceRoleRead(ctx, d, meta)...) + } + + if err != nil { + // TODO: implement error here + // return sdkdiag.AppendErrorf(diags, "updating tags for IAM Role (%s): %s", d.Id(), err) + return + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } // TODO: import state? diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index b85206cae5e..7425d060bef 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -111,22 +111,22 @@ func TestAccIAMRole_description(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - { - Config: testAccRoleConfig_updatedDescription(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "path", "/"), - resource.TestCheckResourceAttr(resourceName, "description", "This 1s an Upd@ted D3scr!pti0n with weird content: &90ë\"'{«¡Çø}"), - ), - }, - { - Config: testAccRoleConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttrSet(resourceName, "create_date"), - resource.TestCheckResourceAttr(resourceName, "description", ""), - ), - }, + // { + // Config: testAccRoleConfig_updatedDescription(rName), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &conf), + // resource.TestCheckResourceAttr(resourceName, "path", "/"), + // resource.TestCheckResourceAttr(resourceName, "description", "This 1s an Upd@ted D3scr!pti0n with weird content: &90ë\"'{«¡Çø}"), + // ), + // }, + // { + // Config: testAccRoleConfig_basic(rName), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &conf), + // resource.TestCheckResourceAttrSet(resourceName, "create_date"), + // resource.TestCheckResourceAttr(resourceName, "description", ""), + // ), + // }, }, }) } From cb6fbbdd29a8549e406be45a55fd24438fcf74e7 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Thu, 18 Jan 2024 23:49:01 -0500 Subject: [PATCH 030/118] Got part of description test done --- internal/service/iam/role.go | 17 +++++++++-- internal/service/iam/role_test.go | 51 +++++-------------------------- 2 files changed, 23 insertions(+), 45 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index ee4759682fd..56d93869e7b 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -486,6 +486,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + fmt.Println("Top of Update") conn := r.Meta().IAMConn(ctx) var plan, state resourceIamRoleData @@ -504,7 +505,7 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } input := &iam.UpdateAssumeRolePolicyInput{ - RoleName: aws.String(plan.ID.ValueString()), + RoleName: aws.String(state.ID.ValueString()), PolicyDocument: aws.String(assumeRolePolicy), } @@ -529,8 +530,10 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } if !plan.Description.Equal(state.Description) { + fmt.Println("Found description change!") + fmt.Println(fmt.Sprintf("Updating to %s", plan.Description.ValueString())) input := &iam.UpdateRoleDescriptionInput{ - RoleName: aws.String(plan.ID.ValueString()), + RoleName: aws.String(state.ID.ValueString()), Description: aws.String(plan.Description.ValueString()), } @@ -539,6 +542,11 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, if err != nil { // TODO: put something there // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) description: %s", d.Id(), err) + fmt.Println("Error updating description") + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionReading, state.ID.String(), plan.Description.String(), err), + err.Error(), + ) return } } @@ -560,7 +568,12 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } } + // TODO: Do we need these? + plan.ID = state.ID + plan.CreateDate = state.CreateDate + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + fmt.Println("Hit bottom of update") } // TODO: import state? diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 7425d060bef..9a69b027c68 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -51,41 +51,6 @@ func TestAccIAMRole_basic(t *testing.T) { }) } -// TODO: comment out later -// func TestAccIAMRole_basic_migration_sdk_to_framework(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" - -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// ExternalProviders: map[string]resource.ExternalProvider{ -// "": { -// VersionConstraint: "5.17.0", -// Source: "hashicorp/terraform-provider-aws", -// }, -// }, -// Config: testAccRoleConfig_basic(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// resource.TestCheckResourceAttr(resourceName, "path", "/"), -// resource.TestCheckResourceAttrSet(resourceName, "create_date"), -// ), -// }, -// { -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// Config: testAccRoleConfig_basic(rName), -// PlanOnly: true, -// }, -// }, -// }) -// } - func TestAccIAMRole_description(t *testing.T) { ctx := acctest.Context(t) var conf iam.Role @@ -111,14 +76,14 @@ func TestAccIAMRole_description(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - // { - // Config: testAccRoleConfig_updatedDescription(rName), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &conf), - // resource.TestCheckResourceAttr(resourceName, "path", "/"), - // resource.TestCheckResourceAttr(resourceName, "description", "This 1s an Upd@ted D3scr!pti0n with weird content: &90ë\"'{«¡Çø}"), - // ), - // }, + { + Config: testAccRoleConfig_updatedDescription(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "description", "This 1s an Upd@ted D3scr!pti0n with weird content: &90ë\"'{«¡Çø}"), + ), + }, // { // Config: testAccRoleConfig_basic(rName), // Check: resource.ComposeTestCheckFunc( From a977ec8c0c56e0c3e5843acbcdea12b36249d17b Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 19 Jan 2024 00:15:13 -0500 Subject: [PATCH 031/118] small improvement --- internal/service/iam/role.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 56d93869e7b..625b2d1ec27 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -14,16 +14,17 @@ 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-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" // "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + // "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" // "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - // "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -36,6 +37,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" "github.com/hashicorp/terraform-provider-aws/internal/verify" + // fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -78,6 +80,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, "id": schema.StringAttribute{ Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "assume_role_policy": schema.StringAttribute{ Required: true, @@ -89,6 +94,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, "create_date": schema.StringAttribute{ Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "description": schema.StringAttribute{ Optional: true, @@ -101,6 +109,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`, // ), }, + // TODO: do something here }, // "force_detach_policies": schema.BoolAttribute{ // Optional: true, @@ -549,6 +558,9 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, ) return } + + // TODO: is this the silver bullet? + state.Description = plan.Description } if !plan.TagsAll.Equal(state.TagsAll) { @@ -569,8 +581,8 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } // TODO: Do we need these? - plan.ID = state.ID - plan.CreateDate = state.CreateDate + // plan.ID = state.ID + // plan.CreateDate = state.CreateDate resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) fmt.Println("Hit bottom of update") From 02575d16c4f993622b60925d8685389091e541a9 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:04:28 -0500 Subject: [PATCH 032/118] got description done --- internal/service/iam/role.go | 3 + internal/service/iam/role_test.go | 1832 ++++++++++++++--------------- 2 files changed, 919 insertions(+), 916 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 625b2d1ec27..84cd9c5945d 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -25,6 +25,7 @@ import ( // "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -100,6 +101,8 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, "description": schema.StringAttribute{ Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), Validators: []validator.String{ stringvalidator.LengthBetween(0, 1000), // TODO: figure this out later for both validators diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 9a69b027c68..41259234b28 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - "github.com/YakDriver/regexache" + // "github.com/YakDriver/regexache" "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" @@ -84,315 +84,49 @@ func TestAccIAMRole_description(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "description", "This 1s an Upd@ted D3scr!pti0n with weird content: &90ë\"'{«¡Çø}"), ), }, - // { - // Config: testAccRoleConfig_basic(rName), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &conf), - // resource.TestCheckResourceAttrSet(resourceName, "create_date"), - // resource.TestCheckResourceAttr(resourceName, "description", ""), - // ), - // }, - }, - }) -} - -func TestAccIAMRole_nameGenerated(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_nameGenerated(), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - acctest.CheckResourceAttrNameGenerated(resourceName, "name"), - resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_namePrefix(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_namePrefix(acctest.ResourcePrefix), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", acctest.ResourcePrefix), - resource.TestCheckResourceAttr(resourceName, "name_prefix", acctest.ResourcePrefix), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_testNameChange(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_pre(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccRoleConfig_post(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - }, - }) -} - -// https://github.com/hashicorp/terraform-provider-aws/issues/23288 -// https://github.com/hashicorp/terraform-provider-aws/issues/28833 -func TestAccIAMRole_diffs(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), - PlanOnly: true, - }, - }, - }) -} - -// https://github.com/hashicorp/terraform-provider-aws/issues/28835 -func TestAccIAMRole_diffsCondition(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_diffsCondition(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffsCondition(rName), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffsCondition(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffsCondition(rName), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffsCondition(rName), + Config: testAccRoleConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttrSet(resourceName, "create_date"), + resource.TestCheckResourceAttr(resourceName, "description", ""), ), }, - { - Config: testAccRoleConfig_diffsCondition(rName), - PlanOnly: true, - }, }, }) } -func TestAccIAMRole_badJSON(t *testing.T) { - ctx := acctest.Context(t) - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// func TestAccIAMRole_nameGenerated(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.Role +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_badJSON(rName), - ExpectError: regexache.MustCompile(`.*contains an invalid JSON policy:.*`), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_nameGenerated(), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// acctest.CheckResourceAttrNameGenerated(resourceName, "name"), +// resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), +// ), +// }, +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// }, +// }, +// }) +// } -// func TestAccIAMRole_disappears(t *testing.T) { +// func TestAccIAMRole_namePrefix(t *testing.T) { // ctx := acctest.Context(t) -// var role iam.Role - -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// var conf iam.Role // resourceName := "aws_iam_role.test" // resource.ParallelTest(t, resource.TestCase{ @@ -402,663 +136,929 @@ func TestAccIAMRole_badJSON(t *testing.T) { // CheckDestroy: testAccCheckRoleDestroy(ctx), // Steps: []resource.TestStep{ // { -// Config: testAccRoleConfig_basic(rName), +// Config: testAccRoleConfig_namePrefix(acctest.ResourcePrefix), // Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), resourceName), +// testAccCheckRoleExists(ctx, resourceName, &conf), +// acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", acctest.ResourcePrefix), +// resource.TestCheckResourceAttr(resourceName, "name_prefix", acctest.ResourcePrefix), // ), -// ExpectNonEmptyPlan: true, +// }, +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, // }, // }, // }) // } -func TestAccIAMRole_policiesForceDetach(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_forceDetachPolicies(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - testAccAddRolePolicy(ctx, resourceName), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"force_detach_policies"}, - }, - }, - }) -} +// func TestAccIAMRole_testNameChange(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" -func TestAccIAMRole_maxSessionDuration(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_pre(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// }, +// { +// Config: testAccRoleConfig_post(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// }, +// }) +// } - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_maxSessionDuration(rName, 3599), - ExpectError: regexache.MustCompile(`expected max_session_duration to be in the range`), - }, - { - Config: testAccRoleConfig_maxSessionDuration(rName, 43201), - ExpectError: regexache.MustCompile(`expected max_session_duration to be in the range`), - }, - { - Config: testAccRoleConfig_maxSessionDuration(rName, 3700), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3700"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccRoleConfig_maxSessionDuration(rName, 3701), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3701"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} +// // https://github.com/hashicorp/terraform-provider-aws/issues/23288 +// // https://github.com/hashicorp/terraform-provider-aws/issues/28833 +// func TestAccIAMRole_diffs(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" -func TestAccIAMRole_permissionsBoundary(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, ""), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffs(rName, "tags = {}"), +// PlanOnly: true, +// }, +// }, +// }) +// } - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// // https://github.com/hashicorp/terraform-provider-aws/issues/28835 +// func TestAccIAMRole_diffsCondition(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - permissionsBoundary1 := fmt.Sprintf("arn:%s:iam::aws:policy/AdministratorAccess", acctest.Partition()) - permissionsBoundary2 := fmt.Sprintf("arn:%s:iam::aws:policy/ReadOnlyAccess", acctest.Partition()) +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_diffsCondition(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffsCondition(rName), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffsCondition(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffsCondition(rName), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_diffsCondition(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// ), +// }, +// { +// Config: testAccRoleConfig_diffsCondition(rName), +// PlanOnly: true, +// }, +// }, +// }) +// } - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckUserDestroy(ctx), - Steps: []resource.TestStep{ - // Test creation - { - Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), - testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), - ), - }, - // Test update - { - Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary2), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary2), - testAccCheckRolePermissionsBoundary(&role, permissionsBoundary2), - ), - }, - // Test import - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{ - "force_destroy", - }, - }, - // Test removal - { - Config: testAccRoleConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "permissions_boundary", ""), - testAccCheckRolePermissionsBoundary(&role, ""), - ), - }, - // Test addition - { - Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), - testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), - ), - }, - // Test drift detection - { - PreConfig: func() { - // delete the boundary manually - conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx) - input := &iam.DeleteRolePermissionsBoundaryInput{ - RoleName: role.RoleName, - } - _, err := conn.DeleteRolePermissionsBoundaryWithContext(ctx, input) - if err != nil { - t.Fatalf("Failed to delete permission_boundary from role (%s): %s", aws.StringValue(role.RoleName), err) - } - }, - Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), - // check the boundary was restored - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), - testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), - ), - }, - // Test empty value - { - Config: testAccRoleConfig_permissionsBoundary(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "permissions_boundary", ""), - testAccCheckRolePermissionsBoundary(&role, ""), - ), - }, - }, - }) -} +// func TestAccIAMRole_badJSON(t *testing.T) { +// ctx := acctest.Context(t) +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -func TestAccIAMRole_tags(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_badJSON(rName), +// ExpectError: regexache.MustCompile(`.*contains an invalid JSON policy:.*`), +// }, +// }, +// }) +// } - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckUserDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_tags(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.tag1", "test-value1"), - resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value2"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccRoleConfig_tagsUpdate(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value"), - ), - }, - }, - }) -} +// func TestAccIAMRole_disappears(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role -func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyInline(rName, policyName1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), - ), - }, - { - Config: testAccRoleConfig_policyInlineUpdate(rName, policyName2, policyName3), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "2"), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), - ), - }, - { - Config: testAccRoleConfig_policyInlineUpdateDown(rName, policyName3), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"inline_policy.0.policy"}, - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_basic(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), resourceName), +// ), +// ExpectNonEmptyPlan: true, +// }, +// }, +// }) +// } -// Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19444 -func TestAccIAMRole_InlinePolicy_ignoreOrder(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// func TestAccIAMRole_policiesForceDetach(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyInlineActionOrder(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), - ), - }, - { - Config: testAccRoleConfig_policyInlineActionOrder(rName), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_policyInlineActionNewOrder(rName), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_policyInlineActionOrderActualDiff(rName), - PlanOnly: true, - ExpectNonEmptyPlan: true, - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_forceDetachPolicies(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// testAccAddRolePolicy(ctx, resourceName), +// ), +// }, +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// ImportStateVerifyIgnore: []string{"force_detach_policies"}, +// }, +// }, +// }) +// } + +// func TestAccIAMRole_maxSessionDuration(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_maxSessionDuration(rName, 3599), +// ExpectError: regexache.MustCompile(`expected max_session_duration to be in the range`), +// }, +// { +// Config: testAccRoleConfig_maxSessionDuration(rName, 43201), +// ExpectError: regexache.MustCompile(`expected max_session_duration to be in the range`), +// }, +// { +// Config: testAccRoleConfig_maxSessionDuration(rName, 3700), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3700"), +// ), +// }, +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// }, +// { +// Config: testAccRoleConfig_maxSessionDuration(rName, 3701), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3701"), +// ), +// }, +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// }, +// }, +// }) +// } + +// func TestAccIAMRole_permissionsBoundary(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role + +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" + +// permissionsBoundary1 := fmt.Sprintf("arn:%s:iam::aws:policy/AdministratorAccess", acctest.Partition()) +// permissionsBoundary2 := fmt.Sprintf("arn:%s:iam::aws:policy/ReadOnlyAccess", acctest.Partition()) + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckUserDestroy(ctx), +// Steps: []resource.TestStep{ +// // Test creation +// { +// Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), +// testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), +// ), +// }, +// // Test update +// { +// Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary2), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary2), +// testAccCheckRolePermissionsBoundary(&role, permissionsBoundary2), +// ), +// }, +// // Test import +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// ImportStateVerifyIgnore: []string{ +// "force_destroy", +// }, +// }, +// // Test removal +// { +// Config: testAccRoleConfig_basic(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", ""), +// testAccCheckRolePermissionsBoundary(&role, ""), +// ), +// }, +// // Test addition +// { +// Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), +// testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), +// ), +// }, +// // Test drift detection +// { +// PreConfig: func() { +// // delete the boundary manually +// conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx) +// input := &iam.DeleteRolePermissionsBoundaryInput{ +// RoleName: role.RoleName, +// } +// _, err := conn.DeleteRolePermissionsBoundaryWithContext(ctx, input) +// if err != nil { +// t.Fatalf("Failed to delete permission_boundary from role (%s): %s", aws.StringValue(role.RoleName), err) +// } +// }, +// Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), +// // check the boundary was restored +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), +// testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), +// ), +// }, +// // Test empty value +// { +// Config: testAccRoleConfig_permissionsBoundary(rName, ""), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", ""), +// testAccCheckRolePermissionsBoundary(&role, ""), +// ), +// }, +// }, +// }) +// } + +// func TestAccIAMRole_tags(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckUserDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_tags(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), +// resource.TestCheckResourceAttr(resourceName, "tags.tag1", "test-value1"), +// resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value2"), +// ), +// }, +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// }, +// { +// Config: testAccRoleConfig_tagsUpdate(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), +// resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value"), +// ), +// }, +// }, +// }) +// } -func TestAccIAMRole_InlinePolicy_empty(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyEmptyInline(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - ), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyInline(rName, policyName1), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), +// resource.TestCheckResourceAttr(resourceName, "name", rName), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), +// ), +// }, +// { +// Config: testAccRoleConfig_policyInlineUpdate(rName, policyName2, policyName3), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "2"), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), +// ), +// }, +// { +// Config: testAccRoleConfig_policyInlineUpdateDown(rName, policyName3), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), +// ), +// }, +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// ImportStateVerifyIgnore: []string{"inline_policy.0.policy"}, +// }, +// }, +// }) +// } -func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19444 +// func TestAccIAMRole_InlinePolicy_ignoreOrder(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyManaged(rName, policyName1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), - ), - }, - { - Config: testAccRoleConfig_policyManagedUpdate(rName, policyName1, policyName2, policyName3), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "2"), - ), - }, - { - Config: testAccRoleConfig_policyManagedUpdateDown(rName, policyName1, policyName2, policyName3), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyInlineActionOrder(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), +// resource.TestCheckResourceAttr(resourceName, "name", rName), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), +// ), +// }, +// { +// Config: testAccRoleConfig_policyInlineActionOrder(rName), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_policyInlineActionNewOrder(rName), +// PlanOnly: true, +// }, +// { +// Config: testAccRoleConfig_policyInlineActionOrderActualDiff(rName), +// PlanOnly: true, +// ExpectNonEmptyPlan: true, +// }, +// }, +// }) +// } -// TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_managedNonEmpty: if a policy is detached -// out of band, it should be reattached. -func TestAccIAMRole_ManagedPolicy_outOfBandRemovalAddedBack(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// func TestAccIAMRole_InlinePolicy_empty(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyManaged(rName, policyName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyDetachManagedPolicy(ctx, &role, policyName), - ), - ExpectNonEmptyPlan: true, - }, - { - Config: testAccRoleConfig_policyManaged(rName, policyName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), - ), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyEmptyInline(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// ), +// }, +// }, +// }) +// } -// TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_inlineNonEmpty: if a policy is removed -// out of band, it should be recreated. -func TestAccIAMRole_InlinePolicy_outOfBandRemovalAddedBack(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyInline(rName, policyName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName), - ), - ExpectNonEmptyPlan: true, - }, - { - Config: testAccRoleConfig_policyInline(rName, policyName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), - ), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyManaged(rName, policyName1), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "name", rName), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), +// ), +// }, +// { +// Config: testAccRoleConfig_policyManagedUpdate(rName, policyName1, policyName2, policyName3), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "2"), +// ), +// }, +// { +// Config: testAccRoleConfig_policyManagedUpdateDown(rName, policyName1, policyName2, policyName3), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), +// ), +// }, +// { +// ResourceName: resourceName, +// ImportState: true, +// ImportStateVerify: true, +// }, +// }, +// }) +// } -// TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemoved: if managed_policy_arns arg -// exists and is non-empty, policy attached out of band should be removed -func TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemoved(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// // TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_managedNonEmpty: if a policy is detached +// // out of band, it should be reattached. +// func TestAccIAMRole_ManagedPolicy_outOfBandRemovalAddedBack(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyExtraManaged(rName, policyName1, policyName2), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName2), - ), - ExpectNonEmptyPlan: true, - }, - { - Config: testAccRoleConfig_policyExtraManaged(rName, policyName1, policyName2), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), - ), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyManaged(rName, policyName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyDetachManagedPolicy(ctx, &role, policyName), +// ), +// ExpectNonEmptyPlan: true, +// }, +// { +// Config: testAccRoleConfig_policyManaged(rName, policyName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), +// ), +// }, +// }, +// }) +// } + +// // TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_inlineNonEmpty: if a policy is removed +// // out of band, it should be recreated. +// func TestAccIAMRole_InlinePolicy_outOfBandRemovalAddedBack(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyInline(rName, policyName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName), +// ), +// ExpectNonEmptyPlan: true, +// }, +// { +// Config: testAccRoleConfig_policyInline(rName, policyName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), +// ), +// }, +// }, +// }) +// } -// TestAccIAMRole_PolicyOutOfBandAdditionRemoved_inlineNonEmpty: if inline_policy arg -// exists and is non-empty, policy added out of band should be removed -func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemoved(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// // TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemoved: if managed_policy_arns arg +// // exists and is non-empty, policy attached out of band should be removed +// func TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemoved(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyInline(rName, policyName1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName2), - ), - ExpectNonEmptyPlan: true, - }, - { - Config: testAccRoleConfig_policyInline(rName, policyName1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), - ), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyExtraManaged(rName, policyName1, policyName2), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName2), +// ), +// ExpectNonEmptyPlan: true, +// }, +// { +// Config: testAccRoleConfig_policyExtraManaged(rName, policyName1, policyName2), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), +// ), +// }, +// }, +// }) +// } -// TestAccIAMRole_PolicyOutOfBandAdditionIgnored_inlineNonExistent: if there is no -// inline_policy attribute, out of band changes should be ignored. -func TestAccIAMRole_InlinePolicy_outOfBandAdditionIgnored(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// // TestAccIAMRole_PolicyOutOfBandAdditionRemoved_inlineNonEmpty: if inline_policy arg +// // exists and is non-empty, policy added out of band should be removed +// func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemoved(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyNoInline(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName1), - ), - }, - { - Config: testAccRoleConfig_policyNoInline(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName2), - ), - }, - { - Config: testAccRoleConfig_policyNoInline(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName1), - testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName2), - ), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyInline(rName, policyName1), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName2), +// ), +// ExpectNonEmptyPlan: true, +// }, +// { +// Config: testAccRoleConfig_policyInline(rName, policyName1), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), +// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), +// ), +// }, +// }, +// }) +// } -// TestAccIAMRole_PolicyOutOfBandAdditionIgnored_managedNonExistent: if there is no -// managed_policy_arns attribute, out of band changes should be ignored. -func TestAccIAMRole_ManagedPolicy_outOfBandAdditionIgnored(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// // TestAccIAMRole_PolicyOutOfBandAdditionIgnored_inlineNonExistent: if there is no +// // inline_policy attribute, out of band changes should be ignored. +// func TestAccIAMRole_InlinePolicy_outOfBandAdditionIgnored(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyNoManaged(rName, policyName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName), - ), - }, - { - Config: testAccRoleConfig_policyNoManaged(rName, policyName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyDetachManagedPolicy(ctx, &role, policyName), - ), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyNoInline(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName1), +// ), +// }, +// { +// Config: testAccRoleConfig_policyNoInline(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName2), +// ), +// }, +// { +// Config: testAccRoleConfig_policyNoInline(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName1), +// testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName2), +// ), +// }, +// }, +// }) +// } -// TestAccIAMRole_PolicyOutOfBandAdditionRemoved_inlineEmpty: if inline is added -// out of band with empty inline arg, should be removed -func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemovedEmpty(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// // TestAccIAMRole_PolicyOutOfBandAdditionIgnored_managedNonExistent: if there is no +// // managed_policy_arns attribute, out of band changes should be ignored. +// func TestAccIAMRole_ManagedPolicy_outOfBandAdditionIgnored(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyEmptyInline(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName), - ), - ExpectNonEmptyPlan: true, - }, - { - Config: testAccRoleConfig_policyEmptyInline(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - ), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyNoManaged(rName, policyName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName), +// ), +// }, +// { +// Config: testAccRoleConfig_policyNoManaged(rName, policyName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyDetachManagedPolicy(ctx, &role, policyName), +// ), +// }, +// }, +// }) +// } -// TestAccIAMRole_PolicyOutOfBandAdditionRemoved_managedEmpty: if managed is attached -// out of band with empty managed arg, should be detached -func TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemovedEmpty(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// // TestAccIAMRole_PolicyOutOfBandAdditionRemoved_inlineEmpty: if inline is added +// // out of band with empty inline arg, should be removed +// func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemovedEmpty(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_policyEmptyManaged(rName, policyName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName), - ), - ExpectNonEmptyPlan: true, - }, - { - Config: testAccRoleConfig_policyEmptyManaged(rName, policyName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - ), - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyEmptyInline(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName), +// ), +// ExpectNonEmptyPlan: true, +// }, +// { +// Config: testAccRoleConfig_policyEmptyInline(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// ), +// }, +// }, +// }) +// } + +// // TestAccIAMRole_PolicyOutOfBandAdditionRemoved_managedEmpty: if managed is attached +// // out of band with empty managed arg, should be detached +// func TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemovedEmpty(t *testing.T) { +// ctx := acctest.Context(t) +// var role iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// Config: testAccRoleConfig_policyEmptyManaged(rName, policyName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName), +// ), +// ExpectNonEmptyPlan: true, +// }, +// { +// Config: testAccRoleConfig_policyEmptyManaged(rName, policyName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &role), +// ), +// }, +// }, +// }) +// } func testAccCheckRoleDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { From 38140522214194b03e35ddb17d6bab0166cf4bc8 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 19 Jan 2024 21:59:55 -0500 Subject: [PATCH 033/118] work so far --- internal/service/iam/role.go | 52 ++++++++++++++++++++----------- internal/service/iam/role_test.go | 52 +++++++++++++++---------------- 2 files changed, 60 insertions(+), 44 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 84cd9c5945d..0924309bfa4 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -145,7 +145,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplaceIfConfigured(), + stringplanmodifier.UseStateForUnknown(), }, + // Default: stringdefault.StaticString(""), Validators: []validator.String{ stringvalidator.LengthAtMost(roleNameMaxLen), // TODO: uncomment when ready @@ -154,23 +156,26 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // ), }, }, - // "name_prefix": schema.StringAttribute{ - // Optional: true, - // Computed: true, - // PlanModifiers: []planmodifier.String{ - // stringplanmodifier.RequiresReplaceIfConfigured(), - // }, - // Validators: []validator.String{ - // stringvalidator.LengthAtMost(roleNamePrefixMaxLen), - // stringvalidator.ConflictsWith( - // path.MatchRelative().AtParent().AtName("name"), - // ), - // }, - // }, + "name_prefix": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplaceIfConfigured(), + stringplanmodifier.UseStateForUnknown(), + }, + Validators: []validator.String{ + stringvalidator.LengthAtMost(roleNamePrefixMaxLen), + stringvalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("name"), + ), + }, + }, "path": schema.StringAttribute{ Optional: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplaceIfConfigured(), + // TODO: can I do this and remove setting in Update/read? + // stringplanmodifier.UseStateForUnknown(), }, // Default: stringdefault.StaticString("/"), Validators: []validator.String{ @@ -203,9 +208,9 @@ type resourceIamRoleData struct { // InlinePolicy types.Map `tfsdk:"inline_policy"` // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` // MaxSessionDuration types.Int64 `tfsdk:"max_session_duration"` - Name types.String `tfsdk:"name"` - // NamePrefix types.String `tfsdk:"name_prefix"` - Path types.String `tfsdk:"path"` + Name types.String `tfsdk:"name"` + NamePrefix types.String `tfsdk:"name_prefix"` + Path types.String `tfsdk:"path"` // PermissionsBoundary types.String `tfsdk:"permissions_boundary"` // UniqueId types.String `tfsdk:"unique_id"` Tags types.Map `tfsdk:"tags"` @@ -232,8 +237,8 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } // TODO: uncomment when we use prefix - // name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) - name := plan.Name.ValueString() + name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) + // name := plan.Name.ValueString() input := &iam.CreateRoleInput{ AssumeRolePolicyDocument: aws.String(assumeRolePolicy), Path: aws.String(plan.Path.ValueString()), @@ -330,6 +335,17 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, plan.CreateDate = flex.StringValueToFramework(ctx, output.Role.CreateDate.Format(time.RFC3339)) plan.ID = flex.StringToFramework(ctx, output.Role.RoleName) + if plan.Name.IsUnknown() { + fmt.Println("Name is Unknown! Setting in plan") + plan.Name = flex.StringToFramework(ctx, output.Role.RoleName) + + if plan.NamePrefix.IsUnknown() { + plan.NamePrefix = flex.StringValueToFramework(ctx, "terraform-") + } + } else { + fmt.Println(fmt.Sprintf("Name: %s", plan.Name.ValueString())) + } + // last steps? // TODO: do we need something?this? // state.refreshFromOutput(ctx, out) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 41259234b28..3a13808b819 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -96,33 +96,33 @@ func TestAccIAMRole_description(t *testing.T) { }) } -// func TestAccIAMRole_nameGenerated(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_nameGenerated(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_nameGenerated(), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// acctest.CheckResourceAttrNameGenerated(resourceName, "name"), -// resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), -// ), -// }, -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_nameGenerated(), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + acctest.CheckResourceAttrNameGenerated(resourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), + ), + }, + // { + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // }, + }, + }) +} // func TestAccIAMRole_namePrefix(t *testing.T) { // ctx := acctest.Context(t) From 022994190cb75d5463677c2bf5ead08dc2fcccd7 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:54:43 -0500 Subject: [PATCH 034/118] finish name prefix --- internal/service/iam/role.go | 32 ++++++++++++++----------------- internal/service/iam/role_test.go | 10 +++++----- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 0924309bfa4..e68ad050281 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -236,9 +236,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, return } - // TODO: uncomment when we use prefix name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) - // name := plan.Name.ValueString() input := &iam.CreateRoleInput{ AssumeRolePolicyDocument: aws.String(assumeRolePolicy), Path: aws.String(plan.Path.ValueString()), @@ -265,9 +263,6 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, // if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { // input.Tags = nil - // output, err = retryCreateRole(ctx, conn, input) - // } - if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), @@ -334,17 +329,19 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, plan.ARN = fwtypes.ARNValue(*output.Role.Arn) plan.CreateDate = flex.StringValueToFramework(ctx, output.Role.CreateDate.Format(time.RFC3339)) plan.ID = flex.StringToFramework(ctx, output.Role.RoleName) + plan.Name = flex.StringToFramework(ctx, output.Role.RoleName) + plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(output.Role.RoleName))) - if plan.Name.IsUnknown() { - fmt.Println("Name is Unknown! Setting in plan") - plan.Name = flex.StringToFramework(ctx, output.Role.RoleName) + // if plan.Name.IsUnknown() { + // fmt.Println("Name is Unknown! Setting in plan") + // plan.Name = flex.StringToFramework(ctx, output.Role.RoleName) - if plan.NamePrefix.IsUnknown() { - plan.NamePrefix = flex.StringValueToFramework(ctx, "terraform-") - } - } else { - fmt.Println(fmt.Sprintf("Name: %s", plan.Name.ValueString())) - } + // // if plan.NamePrefix.IsUnknown() { + // // plan.NamePrefix = flex.StringValueToFramework(ctx, "terraform-") + // // } + // } else { + // fmt.Println(fmt.Sprintf("Name: %s", plan.Name.ValueString())) + // } // last steps? // TODO: do we need something?this? @@ -452,9 +449,9 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.Name = flex.StringToFramework(ctx, role.RoleName) state.ID = flex.StringToFramework(ctx, role.RoleName) state.Description = flex.StringToFramework(ctx, role.Description) + state.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(role.RoleName))) // d.Set("max_session_duration", role.MaxSessionDuration) - // d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(role.RoleName))) // if role.PermissionsBoundary != nil { // d.Set("permissions_boundary", role.PermissionsBoundary.PermissionsBoundaryArn) @@ -599,9 +596,8 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } } - // TODO: Do we need these? - // plan.ID = state.ID - // plan.CreateDate = state.CreateDate + // TODO: do I need this? If so huh? + plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(plan.Name.ValueString())) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) fmt.Println("Hit bottom of update") diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 3a13808b819..9b32517839a 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -115,11 +115,11 @@ func TestAccIAMRole_nameGenerated(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), ), }, - // { - // ResourceName: resourceName, - // ImportState: true, - // ImportStateVerify: true, - // }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } From 37be5cdca6dc1d485d1ae33fbc0ec0b55b02294c Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:58:24 -0500 Subject: [PATCH 035/118] another test --- internal/service/iam/role_test.go | 52 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 9b32517839a..6bfb894b3ad 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -124,33 +124,33 @@ func TestAccIAMRole_nameGenerated(t *testing.T) { }) } -// func TestAccIAMRole_namePrefix(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_namePrefix(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_namePrefix(acctest.ResourcePrefix), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", acctest.ResourcePrefix), -// resource.TestCheckResourceAttr(resourceName, "name_prefix", acctest.ResourcePrefix), -// ), -// }, -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_namePrefix(acctest.ResourcePrefix), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", acctest.ResourcePrefix), + resource.TestCheckResourceAttr(resourceName, "name_prefix", acctest.ResourcePrefix), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} // func TestAccIAMRole_testNameChange(t *testing.T) { // ctx := acctest.Context(t) From 8932b996105dfacfacf8471183e05a9e79e52af8 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:01:41 -0500 Subject: [PATCH 036/118] another test in the books --- internal/service/iam/role_test.go | 62 +++++++++++++++---------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 6bfb894b3ad..416ca4ecda7 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -152,38 +152,38 @@ func TestAccIAMRole_namePrefix(t *testing.T) { }) } -// func TestAccIAMRole_testNameChange(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_testNameChange(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_pre(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// }, -// { -// Config: testAccRoleConfig_post(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_pre(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccRoleConfig_post(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + }, + }) +} // // https://github.com/hashicorp/terraform-provider-aws/issues/23288 // // https://github.com/hashicorp/terraform-provider-aws/issues/28833 From 5eaec8f4b6fcd1226ffb9b0aaa517d8f3eb86633 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:11:01 -0500 Subject: [PATCH 037/118] another test --- internal/service/iam/role_test.go | 268 +++++++++++++++--------------- 1 file changed, 134 insertions(+), 134 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 416ca4ecda7..51e4419419c 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -187,141 +187,141 @@ func TestAccIAMRole_testNameChange(t *testing.T) { // // https://github.com/hashicorp/terraform-provider-aws/issues/23288 // // https://github.com/hashicorp/terraform-provider-aws/issues/28833 -// func TestAccIAMRole_diffs(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_diffs(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, ""), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffs(rName, "tags = {}"), -// PlanOnly: true, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, "tags = {}"), + PlanOnly: true, + }, + }, + }) +} // // https://github.com/hashicorp/terraform-provider-aws/issues/28835 // func TestAccIAMRole_diffsCondition(t *testing.T) { From 009f850fd648e851429ae93e848b6b77f2ec1dbf Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:22:30 -0500 Subject: [PATCH 038/118] another test --- internal/service/iam/role_test.go | 88 +++++++++++++++---------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 51e4419419c..fd1aedcd6e5 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -324,51 +324,51 @@ func TestAccIAMRole_diffs(t *testing.T) { } // // https://github.com/hashicorp/terraform-provider-aws/issues/28835 -// func TestAccIAMRole_diffsCondition(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_diffsCondition(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_diffsCondition(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffsCondition(rName), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffsCondition(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffsCondition(rName), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_diffsCondition(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// ), -// }, -// { -// Config: testAccRoleConfig_diffsCondition(rName), -// PlanOnly: true, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_diffsCondition(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffsCondition(rName), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffsCondition(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffsCondition(rName), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffsCondition(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffsCondition(rName), + PlanOnly: true, + }, + }, + }) +} // func TestAccIAMRole_badJSON(t *testing.T) { // ctx := acctest.Context(t) From a2136369496b199a3657516f7ec1e5278dbe2b3d Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:57:42 -0500 Subject: [PATCH 039/118] update another test --- internal/service/iam/role_test.go | 34 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index fd1aedcd6e5..472c2a9e2f0 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - // "github.com/YakDriver/regexache" + "github.com/YakDriver/regexache" "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" @@ -370,23 +370,23 @@ func TestAccIAMRole_diffsCondition(t *testing.T) { }) } -// func TestAccIAMRole_badJSON(t *testing.T) { -// ctx := acctest.Context(t) -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +func TestAccIAMRole_badJSON(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_badJSON(rName), -// ExpectError: regexache.MustCompile(`.*contains an invalid JSON policy:.*`), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_badJSON(rName), + ExpectError: regexache.MustCompile(`.*Invalid JSON String Value*`), + }, + }, + }) +} // func TestAccIAMRole_disappears(t *testing.T) { // ctx := acctest.Context(t) From 4235d66c6526a0d37890c7442b6266ec03230b87 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 20 Jan 2024 11:45:32 -0500 Subject: [PATCH 040/118] another test --- internal/service/iam/exports_test.go | 1 + internal/service/iam/role_test.go | 44 ++++++++++++++-------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/internal/service/iam/exports_test.go b/internal/service/iam/exports_test.go index b9a3db7b48c..7260aa9a648 100644 --- a/internal/service/iam/exports_test.go +++ b/internal/service/iam/exports_test.go @@ -5,6 +5,7 @@ package iam // Exports for use in tests only. var ( + ResourceRole = newResourceRole ResourceGroupPolicyAttachment = resourceGroupPolicyAttachment ResourcePolicyAttachment = resourcePolicyAttachment ResourceRolePolicyAttachment = resourceRolePolicyAttachment diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 472c2a9e2f0..8375d1f5a1c 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -388,30 +388,30 @@ func TestAccIAMRole_badJSON(t *testing.T) { }) } -// func TestAccIAMRole_disappears(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role +func TestAccIAMRole_disappears(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_basic(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), resourceName), -// ), -// ExpectNonEmptyPlan: true, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} // func TestAccIAMRole_policiesForceDetach(t *testing.T) { // ctx := acctest.Context(t) From 5b81f148678d2d6d36810d21a7c1b8b52c124267 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 20 Jan 2024 14:09:11 -0500 Subject: [PATCH 041/118] force detach policies test --- internal/service/iam/role.go | 58 +++++++++++++++++-------------- internal/service/iam/role_test.go | 54 ++++++++++++++-------------- 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index e68ad050281..83e5ad6d740 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -20,8 +20,11 @@ import ( // "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + // "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" + + // "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" - // "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" // "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" @@ -114,10 +117,14 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, // TODO: do something here }, - // "force_detach_policies": schema.BoolAttribute{ - // Optional: true, - // // Default: booldefault.StaticBool(false), - // }, + "force_detach_policies": schema.BoolAttribute{ + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), + // PlanModifiers: []planmodifier.Bool{ + // boolplanmodifier.UseStateForUnknown(), + // }, + }, // TODO: inline policy goes crazy, have to figure what this type should look like // also read article again // "inline_policy": schema.MapAttribute{ @@ -172,10 +179,12 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, "path": schema.StringAttribute{ Optional: true, + Computed: true, + Default: stringdefault.StaticString("/"), PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplaceIfConfigured(), // TODO: can I do this and remove setting in Update/read? - // stringplanmodifier.UseStateForUnknown(), + stringplanmodifier.UseStateForUnknown(), }, // Default: stringdefault.StaticString("/"), Validators: []validator.String{ @@ -198,12 +207,12 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest } type resourceIamRoleData struct { - ARN fwtypes.ARN `tfsdk:"arn"` - AssumeRolePolicy fwtypes.IAMPolicy `tfsdk:"assume_role_policy"` - CreateDate types.String `tfsdk:"create_date"` - ID types.String `tfsdk:"id"` - Description types.String `tfsdk:"description"` - // ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` + ARN fwtypes.ARN `tfsdk:"arn"` + AssumeRolePolicy fwtypes.IAMPolicy `tfsdk:"assume_role_policy"` + CreateDate types.String `tfsdk:"create_date"` + ID types.String `tfsdk:"id"` + Description types.String `tfsdk:"description"` + ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` // TODO: still have to think this one out // InlinePolicy types.Map `tfsdk:"inline_policy"` // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` @@ -332,17 +341,6 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, plan.Name = flex.StringToFramework(ctx, output.Role.RoleName) plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(output.Role.RoleName))) - // if plan.Name.IsUnknown() { - // fmt.Println("Name is Unknown! Setting in plan") - // plan.Name = flex.StringToFramework(ctx, output.Role.RoleName) - - // // if plan.NamePrefix.IsUnknown() { - // // plan.NamePrefix = flex.StringValueToFramework(ctx, "terraform-") - // // } - // } else { - // fmt.Println(fmt.Sprintf("Name: %s", plan.Name.ValueString())) - // } - // last steps? // TODO: do we need something?this? // state.refreshFromOutput(ctx, out) @@ -373,7 +371,8 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, // } // err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, hasManaged) - err := DeleteRole(ctx, conn, state.Name.ValueString(), false, false, false) + // TODO: should name be ID here? + err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), false, false) if err != nil { // TODO: do something like this to skip deletes on roles that are gone? @@ -451,6 +450,15 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.Description = flex.StringToFramework(ctx, role.Description) state.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(role.RoleName))) + // TODO: how to get around this? + if state.ForceDetachPolicies.IsNull() { + fmt.Println("Hitting here! 2") + // TODO: what? + temp := false + state.ForceDetachPolicies = flex.BoolToFramework(ctx, &temp) + } + fmt.Println(fmt.Sprintf("force detach: %v", state.ForceDetachPolicies.ValueBool())) + // d.Set("max_session_duration", role.MaxSessionDuration) // if role.PermissionsBoundary != nil { @@ -481,8 +489,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } state.AssumeRolePolicy = fwtypes.IAMPolicyValue(policyToSet) - // d.Set("assume_role_policy", policyToSet) - // inlinePolicies, err := readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName), meta) // if err != nil { // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 8375d1f5a1c..5afe631cfdf 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -413,34 +413,34 @@ func TestAccIAMRole_disappears(t *testing.T) { }) } -// func TestAccIAMRole_policiesForceDetach(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_policiesForceDetach(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_forceDetachPolicies(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// testAccAddRolePolicy(ctx, resourceName), -// ), -// }, -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// ImportStateVerifyIgnore: []string{"force_detach_policies"}, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_forceDetachPolicies(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + testAccAddRolePolicy(ctx, resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force_detach_policies"}, + }, + }, + }) +} // func TestAccIAMRole_maxSessionDuration(t *testing.T) { // ctx := acctest.Context(t) From 36eeca36e588b5b212290e3a6380ea64ee32d913 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 20 Jan 2024 14:20:17 -0500 Subject: [PATCH 042/118] small cleanup --- internal/service/iam/role.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 83e5ad6d740..eca282fc1e7 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -450,14 +450,12 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.Description = flex.StringToFramework(ctx, role.Description) state.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(role.RoleName))) - // TODO: how to get around this? if state.ForceDetachPolicies.IsNull() { - fmt.Println("Hitting here! 2") - // TODO: what? + // TODO: better way to do this that is more framework friendly? temp := false state.ForceDetachPolicies = flex.BoolToFramework(ctx, &temp) } - fmt.Println(fmt.Sprintf("force detach: %v", state.ForceDetachPolicies.ValueBool())) + // fmt.Println(fmt.Sprintf("force detach: %v", state.ForceDetachPolicies.ValueBool())) // d.Set("max_session_duration", role.MaxSessionDuration) From e8b9ddc17d17e0b0e775c9314c449cf7428cc985 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:20:30 -0500 Subject: [PATCH 043/118] add max session duration test --- internal/service/iam/role.go | 79 +++++++++++++++----------- internal/service/iam/role_test.go | 92 +++++++++++++++---------------- 2 files changed, 93 insertions(+), 78 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index eca282fc1e7..1b7480f8319 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -14,19 +14,14 @@ 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-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - // "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - // "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" - - // "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" - - // "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" @@ -40,11 +35,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" - "github.com/hashicorp/terraform-provider-aws/internal/verify" - - // fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -70,7 +63,6 @@ func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataR response.TypeName = "aws_iam_role" } -// TODO: Update this func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ @@ -140,13 +132,17 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // // TODO: validate all elements of set are valid arns // // how to do this with helper lib terraform-plugin-framework-validators // }, - // "max_session_duration": schema.Int64Attribute{ - // Optional: true, - // // Default: int64default.StaticInt64(3600), - // Validators: []validator.Int64{ - // int64validator.Between(3600, 43200), - // }, - // }, + "max_session_duration": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(3600), + Validators: []validator.Int64{ + int64validator.Between(3600, 43200), + }, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, "name": schema.StringAttribute{ Optional: true, Computed: true, @@ -213,17 +209,18 @@ type resourceIamRoleData struct { ID types.String `tfsdk:"id"` Description types.String `tfsdk:"description"` ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` + MaxSessionDuration types.Int64 `tfsdk:"max_session_duration"` + Name types.String `tfsdk:"name"` + NamePrefix types.String `tfsdk:"name_prefix"` + Path types.String `tfsdk:"path"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` + // TODO: still have to think this one out // InlinePolicy types.Map `tfsdk:"inline_policy"` // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` - // MaxSessionDuration types.Int64 `tfsdk:"max_session_duration"` - Name types.String `tfsdk:"name"` - NamePrefix types.String `tfsdk:"name_prefix"` - Path types.String `tfsdk:"path"` // PermissionsBoundary types.String `tfsdk:"permissions_boundary"` // UniqueId types.String `tfsdk:"unique_id"` - Tags types.Map `tfsdk:"tags"` - TagsAll types.Map `tfsdk:"tags_all"` } func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -257,9 +254,9 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, input.Description = aws.String(plan.Description.ValueString()) } - // if !plan.MaxSessionDuration.IsNull() { - // input.MaxSessionDuration = aws.Int64(plan.MaxSessionDuration.ValueInt64()) - // } + if !plan.MaxSessionDuration.IsNull() { + input.MaxSessionDuration = aws.Int64(plan.MaxSessionDuration.ValueInt64()) + } // if !plan.PermissionsBoundary.IsNull() { // input.PermissionsBoundary = aws.String(plan.PermissionsBoundary.ValueString()) @@ -449,6 +446,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.ID = flex.StringToFramework(ctx, role.RoleName) state.Description = flex.StringToFramework(ctx, role.Description) state.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(role.RoleName))) + state.MaxSessionDuration = flex.Int64ToFramework(ctx, role.MaxSessionDuration) if state.ForceDetachPolicies.IsNull() { // TODO: better way to do this that is more framework friendly? @@ -457,8 +455,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } // fmt.Println(fmt.Sprintf("force detach: %v", state.ForceDetachPolicies.ValueBool())) - // d.Set("max_session_duration", role.MaxSessionDuration) - // if role.PermissionsBoundary != nil { // d.Set("permissions_boundary", role.PermissionsBoundary.PermissionsBoundaryArn) // } else { @@ -579,10 +575,29 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, return } - // TODO: is this the silver bullet? state.Description = plan.Description } + if !plan.MaxSessionDuration.Equal(state.MaxSessionDuration) { + fmt.Println("Hitting duration update") + fmt.Println(fmt.Sprintf("state: %v", state.MaxSessionDuration.ValueInt64())) + fmt.Println(fmt.Sprintf("plan: %v", plan.MaxSessionDuration.ValueInt64())) + input := &iam.UpdateRoleInput{ + RoleName: aws.String(state.ID.ValueString()), + MaxSessionDuration: aws.Int64(plan.MaxSessionDuration.ValueInt64()), + } + + _, err := conn.UpdateRoleWithContext(ctx, input) + + if err != nil { + // TODO: add error here + fmt.Println("Hit update max session duration error") + return + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) MaxSessionDuration: %s", d.Id(), err) + } + state.MaxSessionDuration = plan.MaxSessionDuration + } + if !plan.TagsAll.Equal(state.TagsAll) { err := roleUpdateTags(ctx, conn, plan.ID.ValueString(), state.TagsAll.Elements(), plan.TagsAll.Elements()) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 5afe631cfdf..f9ed0824da0 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -442,53 +442,53 @@ func TestAccIAMRole_policiesForceDetach(t *testing.T) { }) } -// func TestAccIAMRole_maxSessionDuration(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_maxSessionDuration(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_maxSessionDuration(rName, 3599), -// ExpectError: regexache.MustCompile(`expected max_session_duration to be in the range`), -// }, -// { -// Config: testAccRoleConfig_maxSessionDuration(rName, 43201), -// ExpectError: regexache.MustCompile(`expected max_session_duration to be in the range`), -// }, -// { -// Config: testAccRoleConfig_maxSessionDuration(rName, 3700), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3700"), -// ), -// }, -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// }, -// { -// Config: testAccRoleConfig_maxSessionDuration(rName, 3701), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3701"), -// ), -// }, -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_maxSessionDuration(rName, 3599), + ExpectError: regexache.MustCompile(`Attribute max_session_duration value must be between 3600 and 43200`), + }, + { + Config: testAccRoleConfig_maxSessionDuration(rName, 43201), + ExpectError: regexache.MustCompile(`Attribute max_session_duration value must be between 3600 and 43200`), + }, + { + Config: testAccRoleConfig_maxSessionDuration(rName, 3700), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3700"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccRoleConfig_maxSessionDuration(rName, 3701), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3701"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} // func TestAccIAMRole_permissionsBoundary(t *testing.T) { // ctx := acctest.Context(t) From c215f9daf5347ab9b6f551ea2992245ef2786791 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 20 Jan 2024 16:35:49 -0500 Subject: [PATCH 044/118] part of permissions boundary done --- internal/service/iam/role.go | 68 ++++++++--- internal/service/iam/role_test.go | 183 +++++++++++++++--------------- 2 files changed, 144 insertions(+), 107 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 1b7480f8319..0bf1425059c 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -187,12 +187,15 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest stringvalidator.LengthBetween(0, 512), }, }, - // "permissions_boundary": schema.StringAttribute{ - // Optional: true, - // Validators: []validator.String{ - // fwvalidators.ARN(), - // }, - // }, + "permissions_boundary": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Optional: true, + // Computed: true, + // Default: stringdefault.StaticString(""), + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, // "unique_id": schema.StringAttribute{ // Computed: true, // }, @@ -213,13 +216,13 @@ type resourceIamRoleData struct { Name types.String `tfsdk:"name"` NamePrefix types.String `tfsdk:"name_prefix"` Path types.String `tfsdk:"path"` + PermissionsBoundary fwtypes.ARN `tfsdk:"permissions_boundary"` Tags types.Map `tfsdk:"tags"` TagsAll types.Map `tfsdk:"tags_all"` // TODO: still have to think this one out // InlinePolicy types.Map `tfsdk:"inline_policy"` // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` - // PermissionsBoundary types.String `tfsdk:"permissions_boundary"` // UniqueId types.String `tfsdk:"unique_id"` } @@ -258,9 +261,9 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, input.MaxSessionDuration = aws.Int64(plan.MaxSessionDuration.ValueInt64()) } - // if !plan.PermissionsBoundary.IsNull() { - // input.PermissionsBoundary = aws.String(plan.PermissionsBoundary.ValueString()) - // } + if !plan.PermissionsBoundary.IsNull() { + input.PermissionsBoundary = aws.String(plan.PermissionsBoundary.ValueString()) + } // TODO: uncomment this output, err := retryCreateRole(ctx, conn, input) @@ -453,13 +456,11 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res temp := false state.ForceDetachPolicies = flex.BoolToFramework(ctx, &temp) } - // fmt.Println(fmt.Sprintf("force detach: %v", state.ForceDetachPolicies.ValueBool())) - // if role.PermissionsBoundary != nil { - // d.Set("permissions_boundary", role.PermissionsBoundary.PermissionsBoundaryArn) - // } else { - // d.Set("permissions_boundary", nil) - // } + if role.PermissionsBoundary != nil { + fmt.Println(fmt.Sprintf("permission boundary arn: %v", *role.PermissionsBoundary.PermissionsBoundaryArn)) + state.PermissionsBoundary = fwtypes.ARNValue(*role.PermissionsBoundary.PermissionsBoundaryArn) + } // d.Set("unique_id", role.RoleId) assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) @@ -598,6 +599,41 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, state.MaxSessionDuration = plan.MaxSessionDuration } + if !plan.PermissionsBoundary.Equal(state.PermissionsBoundary) { + fmt.Println("Found PermissionsBoundary diff!") + if !plan.PermissionsBoundary.IsNull() { + fmt.Println("PermissionsBoundary is non empty in plan...") + fmt.Println(fmt.Sprintf("plan pb: %v", plan.PermissionsBoundary.ValueString())) + input := &iam.PutRolePermissionsBoundaryInput{ + PermissionsBoundary: aws.String(plan.PermissionsBoundary.ValueString()), + RoleName: aws.String(state.ID.ValueString()), + } + + _, err := conn.PutRolePermissionsBoundaryWithContext(ctx, input) + + if err != nil { + // TODO: implement this error + return + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) permissions boundary: %s", d.Id(), err) + } + } else { + fmt.Println("PermissionsBoundary is empty removing...") + input := &iam.DeleteRolePermissionsBoundaryInput{ + RoleName: aws.String(state.ID.ValueString()), + } + + _, err := conn.DeleteRolePermissionsBoundaryWithContext(ctx, input) + + if err != nil { + // TODO: implement error + return + // return sdkdiag.AppendErrorf(diags, "deleting IAM Role (%s) permissions boundary: %s", d.Id(), err) + } + } + + state.PermissionsBoundary = plan.PermissionsBoundary + } + if !plan.TagsAll.Equal(state.TagsAll) { err := roleUpdateTags(ctx, conn, plan.ID.ValueString(), state.TagsAll.Elements(), plan.TagsAll.Elements()) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index f9ed0824da0..475c7fdc21a 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -490,100 +490,101 @@ func TestAccIAMRole_maxSessionDuration(t *testing.T) { }) } -// func TestAccIAMRole_permissionsBoundary(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role +func TestAccIAMRole_permissionsBoundary(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// permissionsBoundary1 := fmt.Sprintf("arn:%s:iam::aws:policy/AdministratorAccess", acctest.Partition()) -// permissionsBoundary2 := fmt.Sprintf("arn:%s:iam::aws:policy/ReadOnlyAccess", acctest.Partition()) + permissionsBoundary1 := fmt.Sprintf("arn:%s:iam::aws:policy/AdministratorAccess", acctest.Partition()) + permissionsBoundary2 := fmt.Sprintf("arn:%s:iam::aws:policy/ReadOnlyAccess", acctest.Partition()) -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckUserDestroy(ctx), -// Steps: []resource.TestStep{ -// // Test creation -// { -// Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), -// testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), -// ), -// }, -// // Test update -// { -// Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary2), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary2), -// testAccCheckRolePermissionsBoundary(&role, permissionsBoundary2), -// ), -// }, -// // Test import -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// ImportStateVerifyIgnore: []string{ -// "force_destroy", -// }, -// }, -// // Test removal -// { -// Config: testAccRoleConfig_basic(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", ""), -// testAccCheckRolePermissionsBoundary(&role, ""), -// ), -// }, -// // Test addition -// { -// Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), -// testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), -// ), -// }, -// // Test drift detection -// { -// PreConfig: func() { -// // delete the boundary manually -// conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx) -// input := &iam.DeleteRolePermissionsBoundaryInput{ -// RoleName: role.RoleName, -// } -// _, err := conn.DeleteRolePermissionsBoundaryWithContext(ctx, input) -// if err != nil { -// t.Fatalf("Failed to delete permission_boundary from role (%s): %s", aws.StringValue(role.RoleName), err) -// } -// }, -// Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), -// // check the boundary was restored -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), -// testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), -// ), -// }, -// // Test empty value -// { -// Config: testAccRoleConfig_permissionsBoundary(rName, ""), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "permissions_boundary", ""), -// testAccCheckRolePermissionsBoundary(&role, ""), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckUserDestroy(ctx), + Steps: []resource.TestStep{ + // Test creation + { + Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), + testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), + ), + }, + // Test update + { + Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary2), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary2), + testAccCheckRolePermissionsBoundary(&role, permissionsBoundary2), + ), + }, + // Test import + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "force_destroy", + }, + }, + // Test removal + { + Config: testAccRoleConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + // TODO: what to do here? + resource.TestCheckNoResourceAttr(resourceName, "permissions_boundary"), + testAccCheckRolePermissionsBoundary(&role, ""), + ), + }, + // Test addition + { + Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), + testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), + ), + }, + // Test drift detection + // { + // PreConfig: func() { + // // delete the boundary manually + // conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx) + // input := &iam.DeleteRolePermissionsBoundaryInput{ + // RoleName: role.RoleName, + // } + // _, err := conn.DeleteRolePermissionsBoundaryWithContext(ctx, input) + // if err != nil { + // t.Fatalf("Failed to delete permission_boundary from role (%s): %s", aws.StringValue(role.RoleName), err) + // } + // }, + // Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), + // // check the boundary was restored + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &role), + // resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), + // testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), + // ), + // }, + // // Test empty value + // { + // Config: testAccRoleConfig_permissionsBoundary(rName, ""), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &role), + // resource.TestCheckResourceAttr(resourceName, "permissions_boundary", ""), + // testAccCheckRolePermissionsBoundary(&role, ""), + // ), + // }, + }, + }) +} // func TestAccIAMRole_tags(t *testing.T) { // ctx := acctest.Context(t) From 89ba347c74eb92cbd7d89d4f1479277efcc688c4 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 20 Jan 2024 17:21:12 -0500 Subject: [PATCH 045/118] rest of permission boundary --- internal/service/iam/role.go | 4 +++ internal/service/iam/role_test.go | 55 +++++++++++++++---------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 0bf1425059c..e3aa9aabcd2 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -460,7 +460,11 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res if role.PermissionsBoundary != nil { fmt.Println(fmt.Sprintf("permission boundary arn: %v", *role.PermissionsBoundary.PermissionsBoundaryArn)) state.PermissionsBoundary = fwtypes.ARNValue(*role.PermissionsBoundary.PermissionsBoundaryArn) + } else { + fmt.Println("permission boundary is empty") + state.PermissionsBoundary = fwtypes.ARNNull() } + // d.Set("unique_id", role.RoleId) assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 475c7fdc21a..67e1240e805 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -533,6 +533,11 @@ func TestAccIAMRole_permissionsBoundary(t *testing.T) { "force_destroy", }, }, + // Test empty value + { + Config: testAccRoleConfig_permissionsBoundary(rName, ""), + ExpectError: regexache.MustCompile(`Value "" cannot be parsed as an ARN.`), + }, // Test removal { Config: testAccRoleConfig_basic(rName), @@ -553,35 +558,27 @@ func TestAccIAMRole_permissionsBoundary(t *testing.T) { ), }, // Test drift detection - // { - // PreConfig: func() { - // // delete the boundary manually - // conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx) - // input := &iam.DeleteRolePermissionsBoundaryInput{ - // RoleName: role.RoleName, - // } - // _, err := conn.DeleteRolePermissionsBoundaryWithContext(ctx, input) - // if err != nil { - // t.Fatalf("Failed to delete permission_boundary from role (%s): %s", aws.StringValue(role.RoleName), err) - // } - // }, - // Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), - // // check the boundary was restored - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &role), - // resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), - // testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), - // ), - // }, - // // Test empty value - // { - // Config: testAccRoleConfig_permissionsBoundary(rName, ""), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &role), - // resource.TestCheckResourceAttr(resourceName, "permissions_boundary", ""), - // testAccCheckRolePermissionsBoundary(&role, ""), - // ), - // }, + { + PreConfig: func() { + // delete the boundary manually + conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx) + fmt.Println(fmt.Sprintf("test role name: %s", *role.RoleName)) + input := &iam.DeleteRolePermissionsBoundaryInput{ + RoleName: role.RoleName, + } + _, err := conn.DeleteRolePermissionsBoundaryWithContext(ctx, input) + if err != nil { + t.Fatalf("Failed to delete permission_boundary from role (%s): %s", aws.StringValue(role.RoleName), err) + } + }, + Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), + // check the boundary was restored + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), + testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), + ), + }, }, }) } From 18292b567be803a5582a21ddf0b66d3b69e1429e Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 20 Jan 2024 19:26:02 -0500 Subject: [PATCH 046/118] part of tags test done --- internal/service/iam/role.go | 19 +++++- internal/service/iam/role_test.go | 72 ++++++++++----------- internal/service/iam/service_package_gen.go | 3 + 3 files changed, 55 insertions(+), 39 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index e3aa9aabcd2..0ec68adb091 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -48,6 +48,7 @@ const ( ) // @FrameworkResource(name="Role") +// @Tags(identifierAttribute="id") func newResourceRole(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourceIamRole{} r.SetMigratedFromPluginSDK(true) @@ -246,11 +247,18 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) + + fmt.Println("Loop thru tags in Create...") + tags := getTagsIn(ctx) + fmt.Println(fmt.Sprintf("len tags: %v", len(tags))) + for i := 0; i < len(tags); i++ { + fmt.Println(tags[i]) + } input := &iam.CreateRoleInput{ AssumeRolePolicyDocument: aws.String(assumeRolePolicy), Path: aws.String(plan.Path.ValueString()), RoleName: aws.String(name), - Tags: getTagsIn(ctx), + Tags: tags, } if !plan.Description.IsNull() { @@ -458,10 +466,8 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } if role.PermissionsBoundary != nil { - fmt.Println(fmt.Sprintf("permission boundary arn: %v", *role.PermissionsBoundary.PermissionsBoundaryArn)) state.PermissionsBoundary = fwtypes.ARNValue(*role.PermissionsBoundary.PermissionsBoundaryArn) } else { - fmt.Println("permission boundary is empty") state.PermissionsBoundary = fwtypes.ARNNull() } @@ -509,8 +515,15 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) // } // d.Set("managed_policy_arns", policyARNs) + fmt.Println("Loop thru tags in Read...") + fmt.Println(fmt.Sprintf("len tags: %v", len(role.Tags))) + for i := 0; i < len(role.Tags); i++ { + fmt.Println(role.Tags[i]) + } setTagsOut(ctx, role.Tags) + // state.Tags = flex.FlattenFrameworkStringValueMapLegacy(ctx, KeyValueTags(ctx, role.Tags).Map()) + // data.Tags = flex.FlattenFrameworkStringValueMapLegacy(ctx, tags.Map()) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 67e1240e805..a87eeb0e218 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -583,43 +583,43 @@ func TestAccIAMRole_permissionsBoundary(t *testing.T) { }) } -// func TestAccIAMRole_tags(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_tags(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckUserDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_tags(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), -// resource.TestCheckResourceAttr(resourceName, "tags.tag1", "test-value1"), -// resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value2"), -// ), -// }, -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// }, -// { -// Config: testAccRoleConfig_tagsUpdate(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), -// resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value"), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckUserDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_tags(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.tag1", "test-value1"), + resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + // { + // Config: testAccRoleConfig_tagsUpdate(rName), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &role), + // resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + // resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value"), + // ), + // }, + }, + }) +} // func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { // ctx := acctest.Context(t) diff --git a/internal/service/iam/service_package_gen.go b/internal/service/iam/service_package_gen.go index 0a8bb2a807d..68466c67962 100644 --- a/internal/service/iam/service_package_gen.go +++ b/internal/service/iam/service_package_gen.go @@ -24,6 +24,9 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic { Factory: newResourceRole, Name: "Role", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "id", + }, }, } } From 134593f9171f26eae5491a83e7c0eb6a8d76df75 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 20 Jan 2024 21:19:55 -0500 Subject: [PATCH 047/118] tags test complete and updated diff test --- internal/service/iam/role.go | 29 ++---- internal/service/iam/role_test.go | 149 ++++++++++++++++-------------- internal/service/iam/tags.go | 3 + 3 files changed, 88 insertions(+), 93 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 0ec68adb091..90f96454e0e 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -248,17 +248,11 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, name := create.Name(plan.Name.ValueString(), plan.NamePrefix.ValueString()) - fmt.Println("Loop thru tags in Create...") - tags := getTagsIn(ctx) - fmt.Println(fmt.Sprintf("len tags: %v", len(tags))) - for i := 0; i < len(tags); i++ { - fmt.Println(tags[i]) - } input := &iam.CreateRoleInput{ AssumeRolePolicyDocument: aws.String(assumeRolePolicy), Path: aws.String(plan.Path.ValueString()), RoleName: aws.String(name), - Tags: tags, + Tags: getTagsIn(ctx), } if !plan.Description.IsNull() { @@ -515,11 +509,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) // } // d.Set("managed_policy_arns", policyARNs) - fmt.Println("Loop thru tags in Read...") - fmt.Println(fmt.Sprintf("len tags: %v", len(role.Tags))) - for i := 0; i < len(role.Tags); i++ { - fmt.Println(role.Tags[i]) - } setTagsOut(ctx, role.Tags) // state.Tags = flex.FlattenFrameworkStringValueMapLegacy(ctx, KeyValueTags(ctx, role.Tags).Map()) @@ -573,8 +562,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } if !plan.Description.Equal(state.Description) { - fmt.Println("Found description change!") - fmt.Println(fmt.Sprintf("Updating to %s", plan.Description.ValueString())) input := &iam.UpdateRoleDescriptionInput{ RoleName: aws.String(state.ID.ValueString()), Description: aws.String(plan.Description.ValueString()), @@ -597,9 +584,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } if !plan.MaxSessionDuration.Equal(state.MaxSessionDuration) { - fmt.Println("Hitting duration update") - fmt.Println(fmt.Sprintf("state: %v", state.MaxSessionDuration.ValueInt64())) - fmt.Println(fmt.Sprintf("plan: %v", plan.MaxSessionDuration.ValueInt64())) input := &iam.UpdateRoleInput{ RoleName: aws.String(state.ID.ValueString()), MaxSessionDuration: aws.Int64(plan.MaxSessionDuration.ValueInt64()), @@ -617,10 +601,7 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } if !plan.PermissionsBoundary.Equal(state.PermissionsBoundary) { - fmt.Println("Found PermissionsBoundary diff!") if !plan.PermissionsBoundary.IsNull() { - fmt.Println("PermissionsBoundary is non empty in plan...") - fmt.Println(fmt.Sprintf("plan pb: %v", plan.PermissionsBoundary.ValueString())) input := &iam.PutRolePermissionsBoundaryInput{ PermissionsBoundary: aws.String(plan.PermissionsBoundary.ValueString()), RoleName: aws.String(state.ID.ValueString()), @@ -634,7 +615,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) permissions boundary: %s", d.Id(), err) } } else { - fmt.Println("PermissionsBoundary is empty removing...") input := &iam.DeleteRolePermissionsBoundaryInput{ RoleName: aws.String(state.ID.ValueString()), } @@ -652,20 +632,25 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } if !plan.TagsAll.Equal(state.TagsAll) { - err := roleUpdateTags(ctx, conn, plan.ID.ValueString(), state.TagsAll.Elements(), plan.TagsAll.Elements()) + fmt.Println("Tags are not equal!") + err := roleUpdateTags(ctx, conn, plan.ID.ValueString(), state.TagsAll, plan.TagsAll) // Some partitions (e.g. ISO) may not support tagging. if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { // TODO: implement error here + fmt.Println("Hit error parition updating!") return // return append(diags, resourceRoleRead(ctx, d, meta)...) } if err != nil { + fmt.Println("Hit error updating!") // TODO: implement error here // return sdkdiag.AppendErrorf(diags, "updating tags for IAM Role (%s): %s", d.Id(), err) return } + } else { + fmt.Println("Tags are equal") } // TODO: do I need this? If so huh? diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index a87eeb0e218..bd73c744608 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -187,7 +187,7 @@ func TestAccIAMRole_testNameChange(t *testing.T) { // // https://github.com/hashicorp/terraform-provider-aws/issues/23288 // // https://github.com/hashicorp/terraform-provider-aws/issues/28833 -func TestAccIAMRole_diffs(t *testing.T) { +func TestAccIAMRole_diffsNoCondition(t *testing.T) { ctx := acctest.Context(t) var conf iam.Role rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -199,6 +199,56 @@ func TestAccIAMRole_diffs(t *testing.T) { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckRoleDestroy(ctx), Steps: []resource.TestStep{ + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &conf), + // ), + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // PlanOnly: true, + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &conf), + // ), + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // PlanOnly: true, + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &conf), + // ), + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // PlanOnly: true, + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &conf), + // ), + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // PlanOnly: true, + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &conf), + // ), + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // PlanOnly: true, + // }, { Config: testAccRoleConfig_diffs(rName, ""), Check: resource.ComposeTestCheckFunc( @@ -209,114 +259,71 @@ func TestAccIAMRole_diffs(t *testing.T) { Config: testAccRoleConfig_diffs(rName, ""), PlanOnly: true, }, + // TODO: I don't think you can do this in plugin sdk, either nil or have something + // Either way can be figured out later { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, - { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + // TODO: empty maps are not the same in plugin framework vs sdkv2 + // would have to possibly resolve this in later PR or open to any ideas + // For now if you want no tags would have to set as null + // rest of below test changed to null from `{}` + // Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &conf), ), }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), PlanOnly: true, }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &conf), ), }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), PlanOnly: true, }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &conf), ), }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), PlanOnly: true, }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &conf), ), }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), PlanOnly: true, }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &conf), ), }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), PlanOnly: true, }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &conf), ), }, { - Config: testAccRoleConfig_diffs(rName, "tags = {}"), + Config: testAccRoleConfig_diffs(rName, "tags = null"), PlanOnly: true, }, }, @@ -609,14 +616,14 @@ func TestAccIAMRole_tags(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - // { - // Config: testAccRoleConfig_tagsUpdate(rName), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &role), - // resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - // resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value"), - // ), - // }, + { + Config: testAccRoleConfig_tagsUpdate(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.tag2", "test-value"), + ), + }, }, }) } diff --git a/internal/service/iam/tags.go b/internal/service/iam/tags.go index fef0ebb1047..1b69528b4d0 100644 --- a/internal/service/iam/tags.go +++ b/internal/service/iam/tags.go @@ -153,6 +153,9 @@ func policyCreateTags(ctx context.Context, conn iamiface.IAMAPI, identifier stri func roleUpdateTags(ctx context.Context, conn iamiface.IAMAPI, identifier string, oldTagsMap, newTagsMap any) error { oldTags := tftags.New(ctx, oldTagsMap) newTags := tftags.New(ctx, newTagsMap) + fmt.Println("In update role tags") + fmt.Println(fmt.Sprintf("Old tags: %+v", oldTags)) + fmt.Println(fmt.Sprintf("Old tags: %+v", newTags)) if removedTags := oldTags.Removed(newTags).IgnoreSystem(names.IAM); len(removedTags) > 0 { input := &iam.UntagRoleInput{ From 5b41565268ff2388c8cd66674972ed163791cb6e Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 00:51:24 -0500 Subject: [PATCH 048/118] work so far to get inline policies working, have someting read in --- internal/service/iam/role.go | 171 +++++++++++++++++++++++------- internal/service/iam/role_test.go | 104 +++++++++--------- 2 files changed, 184 insertions(+), 91 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 90f96454e0e..846500eebd2 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -14,6 +14,7 @@ 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/go-multierror" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" @@ -75,6 +76,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest stringplanmodifier.UseStateForUnknown(), }, }, + // TODO: should this be this? + // "github.com/hashicorp/terraform-provider-aws/internal/framework" + //framework.IDAttribute() "id": schema.StringAttribute{ Computed: true, PlanModifiers: []planmodifier.String{ @@ -118,13 +122,10 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // boolplanmodifier.UseStateForUnknown(), // }, }, - // TODO: inline policy goes crazy, have to figure what this type should look like - // also read article again - // "inline_policy": schema.MapAttribute{ - // ElementType: types.StringType, - // Optional: true, - // // TODO: maybe some validation? - // }, + "inline_policies": schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + }, // "managed_policy_arns": schema.SetAttribute{ // Computed: true, // Optional: true, @@ -220,9 +221,9 @@ type resourceIamRoleData struct { PermissionsBoundary fwtypes.ARN `tfsdk:"permissions_boundary"` Tags types.Map `tfsdk:"tags"` TagsAll types.Map `tfsdk:"tags_all"` + InlinePolicies types.Map `tfsdk:"inline_policies"` // TODO: still have to think this one out - // InlinePolicy types.Map `tfsdk:"inline_policy"` // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` // UniqueId types.String `tfsdk:"unique_id"` } @@ -282,21 +283,25 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, return } - // roleName := aws.StringValue(output.Role.RoleName) - - // TODO: has to figure this out because typing of inline policies - // if !plan.InlinePolicy.IsNull() && !plan.InlinePolicy.IsUnknown() { - // inline_policies_map := make(map[string]string) - // plan.InlinePolicy.ElementsAs(ctx, inline_policies_map, false) - // policies := expandRoleInlinePolicies(roleName, inline_policies_map) - // if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { - // resp.Diagnostics.AddError( - // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), - // err.Error(), - // ) - // return - // } - // } + roleName := aws.StringValue(output.Role.RoleName) + + if !plan.InlinePolicies.IsNull() && !plan.InlinePolicies.IsUnknown() { + fmt.Println("Found Inline Policies!") + inline_policies_map := make(map[string]string) + plan.InlinePolicies.ElementsAs(ctx, &inline_policies_map, false) + // v, _ := plan.InlinePolicies.ToMapValue(ctx) + fmt.Println(fmt.Sprintf("len inline_policies_map: %v", len(inline_policies_map))) + fmt.Println(fmt.Sprintf("inline_policies_map: %+v", inline_policies_map)) + policies := expandRoleInlinePolicies(roleName, inline_policies_map) + fmt.Println(fmt.Sprintf("policies: %+v", policies)) + // if err := r.addRoleInlinePolicies(ctx, policies); err != nil { + // resp.Diagnostics.AddError( + // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + // err.Error(), + // ) + // return + // } + } // if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { // managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) @@ -309,14 +314,6 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, // } // } - // TODO: do something with this? - // some resources have been created but not all attributes - // d.SetId(roleName) - // state := plan - // // TODO: do we need this? - // // state.refreshFromOutput(ctx, out) - // resp.Diagnostics.Append(resp.State.Set(ctx, state)...) - // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { err := roleCreateTags(ctx, conn, name, tags) @@ -362,10 +359,10 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, return } - // hasInline := false - // if !state.InlinePolicy.IsNull() && !state.InlinePolicy.IsUnknown() { - // hasInline = true - // } + hasInline := false + if !state.InlinePolicies.IsNull() && !state.InlinePolicies.IsUnknown() { + hasInline = true + } // hasManaged := false // if !state.ManagedPolicyArns.IsNull() && !state.ManagedPolicyArns.IsUnknown() { @@ -374,7 +371,7 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, // err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, hasManaged) // TODO: should name be ID here? - err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), false, false) + err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, false) if err != nil { // TODO: do something like this to skip deletes on roles that are gone? @@ -490,7 +487,9 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res // inlinePolicies, err := readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName), meta) // if err != nil { - // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) + // // TODO: figure out this error + // return + // // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) // } // var configPoliciesList []*iam.PutRolePolicyInput @@ -653,6 +652,9 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, fmt.Println("Tags are equal") } + if !plan.TagsAll.Equal(state.TagsAll) { + } + // TODO: do I need this? If so huh? plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(plan.Name.ValueString())) @@ -660,9 +662,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, fmt.Println("Hit bottom of update") } -// TODO: import state? -// https://developer.hashicorp.com/terraform/plugin/framework/resources/import - func FindRoleByName(ctx context.Context, conn *iam.IAM, name string) (*iam.Role, error) { input := &iam.GetRoleInput{ RoleName: aws.String(name), @@ -925,3 +924,95 @@ func deleteRoleInlinePolicies(ctx context.Context, conn *iam.IAM, roleName strin return errors.Join(errs...) } + +func expandRoleInlinePolicies(roleName string, tfPoliciesMap map[string]string) []*iam.PutRolePolicyInput { + if len(tfPoliciesMap) == 0 { + return nil + } + + var apiObjects []*iam.PutRolePolicyInput + + for policyName, policyDocument := range tfPoliciesMap { + fmt.Println(fmt.Sprintf("policyName: %s", policyName)) + fmt.Println(fmt.Sprintf("policyDocument: %s", policyDocument)) + apiObject := expandRoleInlinePolicy(roleName, policyName, policyDocument) + + if apiObject == nil { + continue + } + + apiObjects = append(apiObjects, apiObject) + } + + return apiObjects +} + +func expandRoleInlinePolicy(roleName string, policyName string, policyDocument string) *iam.PutRolePolicyInput { + apiObject := &iam.PutRolePolicyInput{} + + apiObject.PolicyName = aws.String(policyName) + apiObject.PolicyDocument = aws.String(policyDocument) + apiObject.RoleName = aws.String(roleName) + + return apiObject +} + +func (r resourceIamRole) addRoleInlinePolicies(ctx context.Context, policies []*iam.PutRolePolicyInput) error { + conn := r.Meta().IAMConn(ctx) + + var errs *multierror.Error + for _, policy := range policies { + if len(aws.StringValue(policy.PolicyName)) == 0 || len(aws.StringValue(policy.PolicyDocument)) == 0 { + continue + } + + if _, err := conn.PutRolePolicyWithContext(ctx, policy); err != nil { + newErr := fmt.Errorf("adding inline policy (%s): %w", aws.StringValue(policy.PolicyName), err) + errs = multierror.Append(errs, newErr) + } + } + + return errs.ErrorOrNil() +} + +// func (r resourceIamRole) readRoleInlinePolicies(ctx context.Context, roleName string) ([]*iam.PutRolePolicyInput, error) { +// conn := r.Meta().IAMConn(ctx) + +// policyNames, err := findRolePolicyNames(ctx, conn, roleName) + +// if err != nil { +// return nil, err +// } + +// var apiObjects []*iam.PutRolePolicyInput +// for _, policyName := range policyNames { +// output, err := conn.GetRolePolicyWithContext(ctx, &iam.GetRolePolicyInput{ +// RoleName: aws.String(roleName), +// PolicyName: aws.String(policyName), +// }) + +// if err != nil { +// return nil, err +// } + +// policy, err := url.QueryUnescape(aws.StringValue(output.PolicyDocument)) +// if err != nil { +// return nil, err +// } + +// p, err := verify.LegacyPolicyNormalize(policy) +// if err != nil { +// return nil, fmt.Errorf("policy (%s) is invalid JSON: %w", p, err) +// } + +// apiObject := &iam.PutRolePolicyInput{ +// RoleName: aws.String(roleName), +// PolicyDocument: aws.String(p), +// PolicyName: aws.String(policyName), +// } + +// apiObjects = append(apiObjects, apiObject) +// } + +// return apiObjects, nil +// } diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index bd73c744608..503395565f8 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -628,55 +628,55 @@ func TestAccIAMRole_tags(t *testing.T) { }) } -// func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyInline(rName, policyName1), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), -// resource.TestCheckResourceAttr(resourceName, "name", rName), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), -// ), -// }, -// { -// Config: testAccRoleConfig_policyInlineUpdate(rName, policyName2, policyName3), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "2"), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), -// ), -// }, -// { -// Config: testAccRoleConfig_policyInlineUpdateDown(rName, policyName3), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), -// ), -// }, -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// ImportStateVerifyIgnore: []string{"inline_policy.0.policy"}, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyInline(rName, policyName1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + ), + }, + // { + // Config: testAccRoleConfig_policyInlineUpdate(rName, policyName2, policyName3), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &role), + // resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "2"), + // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + // ), + // }, + // { + // Config: testAccRoleConfig_policyInlineUpdateDown(rName, policyName3), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &role), + // resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), + // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + // ), + // }, + // { + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{"inline_policy.0.policy"}, + // }, + }, + }) +} // // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19444 // func TestAccIAMRole_InlinePolicy_ignoreOrder(t *testing.T) { @@ -1790,10 +1790,12 @@ resource "aws_iam_role" "test" { }] }) - inline_policy { - name = %[2]q + // inline_policies = { + // "derp" = %[2]q + // } - policy = < Date: Sun, 21 Jan 2024 10:51:06 -0500 Subject: [PATCH 049/118] starting inline policy journey --- internal/service/iam/role.go | 18 +++++++++--------- internal/service/iam/role_test.go | 9 +++------ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 846500eebd2..d104b7ea048 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -291,16 +291,16 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, plan.InlinePolicies.ElementsAs(ctx, &inline_policies_map, false) // v, _ := plan.InlinePolicies.ToMapValue(ctx) fmt.Println(fmt.Sprintf("len inline_policies_map: %v", len(inline_policies_map))) - fmt.Println(fmt.Sprintf("inline_policies_map: %+v", inline_policies_map)) + // fmt.Println(fmt.Sprintf("inline_policies_map: %+v", inline_policies_map)) policies := expandRoleInlinePolicies(roleName, inline_policies_map) - fmt.Println(fmt.Sprintf("policies: %+v", policies)) - // if err := r.addRoleInlinePolicies(ctx, policies); err != nil { - // resp.Diagnostics.AddError( - // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), - // err.Error(), - // ) - // return - // } + // fmt.Println(fmt.Sprintf("policies: %+v", policies)) + if err := r.addRoleInlinePolicies(ctx, policies); err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + err.Error(), + ) + return + } } // if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 503395565f8..815463eb3ac 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -647,9 +647,10 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { Config: testAccRoleConfig_policyInline(rName, policyName1), Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + // TODO: remove this once we add managed_policy_arns + // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), ), }, // { @@ -1790,10 +1791,6 @@ resource "aws_iam_role" "test" { }] }) - // inline_policies = { - // "derp" = %[2]q - // } - inline_policies = { %[2]q = < Date: Sun, 21 Jan 2024 11:57:59 -0500 Subject: [PATCH 050/118] work so far to get next steps working --- internal/service/iam/role.go | 122 +++++++++++++++++++++--------- internal/service/iam/role_test.go | 53 ++++++++----- 2 files changed, 121 insertions(+), 54 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index d104b7ea048..8e1be665349 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -14,6 +14,7 @@ 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" + awspolicy "github.com/hashicorp/awspolicyequivalence" "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -268,7 +269,6 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, input.PermissionsBoundary = aws.String(plan.PermissionsBoundary.ValueString()) } - // TODO: uncomment this output, err := retryCreateRole(ctx, conn, input) // TODO: So this needs tags... do we need on resourceIamRoleData? @@ -630,6 +630,56 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, state.PermissionsBoundary = plan.PermissionsBoundary } + if !plan.InlinePolicies.Equal(state.InlinePolicies) && inlinePoliciesActualDiff(ctx, &plan, &state) { + fmt.Println("Found inline policies changes!") + + old_inline_policies_map := make(map[string]string) + state.InlinePolicies.ElementsAs(ctx, &old_inline_policies_map, false) + + new_inline_policies_map := make(map[string]string) + plan.InlinePolicies.ElementsAs(ctx, &new_inline_policies_map, false) + + var remove []string + // TODO: get diffs correctly + for k := range old_inline_policies_map { + _, ok := new_inline_policies_map[k] + // If the key exists + if !ok { + fmt.Println(fmt.Sprintf("Adding inline policy to remove: %s", k)) + remove = append(remove, k) + } + } + + // var add []string + // remove := os.Difference(ns).List() + // add := ns.Difference(os).List() + + // roleName := state.Name.ValueString() + // osPolicies := expandRoleInlinePolicies(roleName, old_inline_policies_map) + // nsPolicies := expandRoleInlinePolicies(roleName, new_inline_policies_map) + + // var policyNames []string + // for _, policy := range remove { + // tfMap, ok := policy.(map[string]interface{}) + + // if !ok { + // continue + // } + + // if v, ok := tfMap["name"].(string); ok && v != "" { + // policyNames = append(policyNames, tfMap["name"].(string)) + // } + // } + // if err := deleteRoleInlinePolicies(ctx, conn, roleName, policyNames); err != nil { + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) + // } + + // policies := expandRoleInlinePolicies(roleName, add) + // if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) + // } + } + if !plan.TagsAll.Equal(state.TagsAll) { fmt.Println("Tags are not equal!") err := roleUpdateTags(ctx, conn, plan.ID.ValueString(), state.TagsAll, plan.TagsAll) @@ -655,7 +705,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, if !plan.TagsAll.Equal(state.TagsAll) { } - // TODO: do I need this? If so huh? plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(plan.Name.ValueString())) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) @@ -975,44 +1024,49 @@ func (r resourceIamRole) addRoleInlinePolicies(ctx context.Context, policies []* return errs.ErrorOrNil() } -// func (r resourceIamRole) readRoleInlinePolicies(ctx context.Context, roleName string) ([]*iam.PutRolePolicyInput, error) { -// conn := r.Meta().IAMConn(ctx) +func inlinePoliciesActualDiff(ctx context.Context, plan *resourceIamRoleData, state *resourceIamRoleData) bool { + roleName := state.Name.ValueString() -// policyNames, err := findRolePolicyNames(ctx, conn, roleName) + old_inline_policies_map := make(map[string]string) + state.InlinePolicies.ElementsAs(ctx, &old_inline_policies_map, false) -// if err != nil { -// return nil, err -// } + new_inline_policies_map := make(map[string]string) + plan.InlinePolicies.ElementsAs(ctx, &new_inline_policies_map, false) -// var apiObjects []*iam.PutRolePolicyInput -// for _, policyName := range policyNames { -// output, err := conn.GetRolePolicyWithContext(ctx, &iam.GetRolePolicyInput{ -// RoleName: aws.String(roleName), -// PolicyName: aws.String(policyName), -// }) + osPolicies := expandRoleInlinePolicies(roleName, old_inline_policies_map) + nsPolicies := expandRoleInlinePolicies(roleName, new_inline_policies_map) -// if err != nil { -// return nil, err -// } + return !inlinePoliciesEquivalent(nsPolicies, osPolicies) +} -// policy, err := url.QueryUnescape(aws.StringValue(output.PolicyDocument)) -// if err != nil { -// return nil, err -// } +func inlinePoliciesEquivalent(readPolicies, configPolicies []*iam.PutRolePolicyInput) bool { + if readPolicies == nil && configPolicies == nil { + return true + } + + if len(readPolicies) == 0 && len(configPolicies) == 1 { + if equivalent, err := awspolicy.PoliciesAreEquivalent(`{}`, aws.StringValue(configPolicies[0].PolicyDocument)); err == nil && equivalent { + return true + } + } -// p, err := verify.LegacyPolicyNormalize(policy) -// if err != nil { -// return nil, fmt.Errorf("policy (%s) is invalid JSON: %w", p, err) -// } + if len(readPolicies) != len(configPolicies) { + return false + } -// apiObject := &iam.PutRolePolicyInput{ -// RoleName: aws.String(roleName), -// PolicyDocument: aws.String(p), -// PolicyName: aws.String(policyName), -// } + matches := 0 -// apiObjects = append(apiObjects, apiObject) -// } + for _, policyOne := range readPolicies { + for _, policyTwo := range configPolicies { + if aws.StringValue(policyOne.PolicyName) == aws.StringValue(policyTwo.PolicyName) { + matches++ + if equivalent, err := awspolicy.PoliciesAreEquivalent(aws.StringValue(policyOne.PolicyDocument), aws.StringValue(policyTwo.PolicyDocument)); err != nil || !equivalent { + return false + } + break + } + } + } -// return apiObjects, nil -// } + return matches == len(readPolicies) +} diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 815463eb3ac..f223f39301e 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -633,8 +633,8 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { var role iam.Role rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - // policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - // policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_iam_role.test" resource.ParallelTest(t, resource.TestCase{ @@ -650,17 +650,20 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), resource.TestCheckResourceAttr(resourceName, "name", rName), // TODO: remove this once we add managed_policy_arns + // should be null? + // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + ), + }, + { + Config: testAccRoleConfig_policyInlineUpdate(rName, policyName2, policyName3), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "2"), + // TODO: remove this once we add managed_policy_arns + // should be null? // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), ), }, - // { - // Config: testAccRoleConfig_policyInlineUpdate(rName, policyName2, policyName3), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &role), - // resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "2"), - // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), - // ), - // }, // { // Config: testAccRoleConfig_policyInlineUpdateDown(rName, policyName3), // Check: resource.ComposeTestCheckFunc( @@ -1815,6 +1818,23 @@ func testAccRoleConfig_policyInlineUpdate(roleName, policyName2, policyName3 str return fmt.Sprintf(` data "aws_partition" "current" {} +locals { + inline_policy_doc = < Date: Sun, 21 Jan 2024 15:05:39 -0500 Subject: [PATCH 051/118] more work --- internal/service/iam/role.go | 189 ++++++++++++++++++++++-------- internal/service/iam/role_test.go | 41 ++++--- 2 files changed, 163 insertions(+), 67 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 8e1be665349..6ae1a94aca4 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -462,6 +462,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.PermissionsBoundary = fwtypes.ARNNull() } + // TODO: have to add this and maybe a test to validate this is non empty in basic // d.Set("unique_id", role.RoleId) assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) @@ -485,23 +486,30 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } state.AssumeRolePolicy = fwtypes.IAMPolicyValue(policyToSet) - // inlinePolicies, err := readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName), meta) - // if err != nil { - // // TODO: figure out this error - // return - // // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) - // } + inlinePolicies, err := r.readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName)) + if err != nil { + // TODO: figure out this error + return + // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) + } - // var configPoliciesList []*iam.PutRolePolicyInput - // if v := d.Get("inline_policy").(*schema.Set); v.Len() > 0 { - // configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), v.List()) - // } + var configPoliciesList []*iam.PutRolePolicyInput + if !state.InlinePolicies.IsNull() && !state.ARN.IsUnknown() { + inline_policies_map := make(map[string]string) + state.InlinePolicies.ElementsAs(ctx, &inline_policies_map, false) + configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), inline_policies_map) + } - // if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { - // if err := d.Set("inline_policy", flattenRoleInlinePolicies(inlinePolicies)); err != nil { - // return sdkdiag.AppendErrorf(diags, "setting inline_policy: %s", err) - // } - // } + if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { + fmt.Println("found different inline policies!") + state.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, flattenRoleInlinePolicies(inlinePolicies)) + // if err := d.Set("inline_policy", flattenRoleInlinePolicies(inlinePolicies)); err != nil { + // // TODO: make error here + // fmt.Printf("error flattening inline policies in read") + // return + // // return sdkdiag.AppendErrorf(diags, "setting inline_policy: %s", err) + // } + } // policyARNs, err := findRoleAttachedPolicies(ctx, conn, d.Id()) // if err != nil { @@ -639,45 +647,73 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, new_inline_policies_map := make(map[string]string) plan.InlinePolicies.ElementsAs(ctx, &new_inline_policies_map, false) - var remove []string - // TODO: get diffs correctly + fmt.Println(fmt.Sprintf("len old_inline_policies_map: %v", len(old_inline_policies_map))) + fmt.Println(fmt.Sprintf("len new_inline_policies_map: %v", len(new_inline_policies_map))) + + var remove_policy_names []string for k := range old_inline_policies_map { - _, ok := new_inline_policies_map[k] + if _, ok := new_inline_policies_map[k]; !ok { + fmt.Println(fmt.Sprintf("Found inline policy to remove: %s", k)) + remove_policy_names = append(remove_policy_names, k) + } + } + + // need set like object to store policy names we want to add + add_policy_names := make(map[string]int64) + fmt.Println("Going into add_policy_names loop...") + for k, v := range new_inline_policies_map { + fmt.Println(fmt.Sprintf("Looking at key: %s", k)) + val, ok := old_inline_policies_map[k] // If the key exists if !ok { - fmt.Println(fmt.Sprintf("Adding inline policy to remove: %s", k)) - remove = append(remove, k) + fmt.Println(fmt.Sprintf("Found inline policy to add that does not exist: %s", k)) + add_policy_names[k] = 0 + continue } - } - // var add []string - // remove := os.Difference(ns).List() - // add := ns.Difference(os).List() + if equivalent, err := awspolicy.PoliciesAreEquivalent(aws.StringValue(&v), aws.StringValue(&val)); err != nil || !equivalent { + fmt.Println(fmt.Sprintf("Found inline policy to add that needs update: %s", k)) + add_policy_names[k] = 0 + } + } - // roleName := state.Name.ValueString() - // osPolicies := expandRoleInlinePolicies(roleName, old_inline_policies_map) - // nsPolicies := expandRoleInlinePolicies(roleName, new_inline_policies_map) + fmt.Println(fmt.Sprintf("len add_policy_names: %v", len(add_policy_names))) + fmt.Println(fmt.Sprintf("add_policy_names values: %+v", add_policy_names)) + fmt.Println(fmt.Sprintf("new_inline_policies_map: %+v", new_inline_policies_map)) + + roleName := state.Name.ValueString() + nsPolicies := expandRoleInlinePolicies(roleName, new_inline_policies_map) + fmt.Println(fmt.Sprintf("nsPolicies: %+v", nsPolicies)) + + // getting policy objects we want to add based on add_policy_names map + var add_policies []*iam.PutRolePolicyInput + fmt.Println("Going into add_policies names loop...") + for _, val := range nsPolicies { + fmt.Println(fmt.Sprintf("Looking at: %s", *val.PolicyName)) + if _, ok := add_policy_names[*val.PolicyName]; ok { + fmt.Println("adding") + add_policies = append(add_policies, val) + } else { + fmt.Println("not found") + } + } - // var policyNames []string - // for _, policy := range remove { - // tfMap, ok := policy.(map[string]interface{}) + fmt.Println(fmt.Sprintf("len add_policies: %v", len(add_policies))) + fmt.Println(fmt.Sprintf("len remove_policy_names: %v", len(remove_policy_names))) - // if !ok { - // continue - // } + // Always add before delete + if err := r.addRoleInlinePolicies(ctx, add_policies); err != nil { + // TODO: add error here + return + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) + } - // if v, ok := tfMap["name"].(string); ok && v != "" { - // policyNames = append(policyNames, tfMap["name"].(string)) - // } - // } - // if err := deleteRoleInlinePolicies(ctx, conn, roleName, policyNames); err != nil { - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) - // } + if err := deleteRoleInlinePolicies(ctx, conn, roleName, remove_policy_names); err != nil { + // TODO: add error here + return + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) + } - // policies := expandRoleInlinePolicies(roleName, add) - // if err := addRoleInlinePolicies(ctx, policies, meta); err != nil { - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) - // } } if !plan.TagsAll.Equal(state.TagsAll) { @@ -702,9 +738,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, fmt.Println("Tags are equal") } - if !plan.TagsAll.Equal(state.TagsAll) { - } - plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(plan.Name.ValueString())) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) @@ -983,7 +1016,7 @@ func expandRoleInlinePolicies(roleName string, tfPoliciesMap map[string]string) for policyName, policyDocument := range tfPoliciesMap { fmt.Println(fmt.Sprintf("policyName: %s", policyName)) - fmt.Println(fmt.Sprintf("policyDocument: %s", policyDocument)) + // fmt.Println(fmt.Sprintf("policyDocument: %s", policyDocument)) apiObject := expandRoleInlinePolicy(roleName, policyName, policyDocument) if apiObject == nil { @@ -1070,3 +1103,63 @@ func inlinePoliciesEquivalent(readPolicies, configPolicies []*iam.PutRolePolicyI return matches == len(readPolicies) } + +func (r resourceIamRole) readRoleInlinePolicies(ctx context.Context, roleName string) ([]*iam.PutRolePolicyInput, error) { + conn := r.Meta().IAMConn(ctx) + + policyNames, err := findRolePolicyNames(ctx, conn, roleName) + + if err != nil { + return nil, err + } + + var apiObjects []*iam.PutRolePolicyInput + for _, policyName := range policyNames { + output, err := conn.GetRolePolicyWithContext(ctx, &iam.GetRolePolicyInput{ + RoleName: aws.String(roleName), + PolicyName: aws.String(policyName), + }) + + if err != nil { + return nil, err + } + + policy, err := url.QueryUnescape(aws.StringValue(output.PolicyDocument)) + if err != nil { + return nil, err + } + + p, err := verify.LegacyPolicyNormalize(policy) + if err != nil { + return nil, fmt.Errorf("policy (%s) is invalid JSON: %w", p, err) + } + + apiObject := &iam.PutRolePolicyInput{ + RoleName: aws.String(roleName), + PolicyDocument: aws.String(p), + PolicyName: aws.String(policyName), + } + + apiObjects = append(apiObjects, apiObject) + } + + return apiObjects, nil +} + +func flattenRoleInlinePolicies(apiObjects []*iam.PutRolePolicyInput) map[string]string { + if len(apiObjects) == 0 { + return nil + } + + tfMap := make(map[string]string) + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfMap[aws.StringValue(apiObject.PolicyName)] = aws.StringValue(apiObject.PolicyDocument) + } + + return tfMap +} diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index f223f39301e..d3f06f31c4c 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -199,6 +199,17 @@ func TestAccIAMRole_diffsNoCondition(t *testing.T) { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckRoleDestroy(ctx), Steps: []resource.TestStep{ + // TODO: have to validate this... + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &conf), + // ), + // }, + // { + // Config: testAccRoleConfig_diffs(rName, ""), + // PlanOnly: true, + // }, // { // Config: testAccRoleConfig_diffs(rName, ""), // Check: resource.ComposeTestCheckFunc( @@ -249,16 +260,6 @@ func TestAccIAMRole_diffsNoCondition(t *testing.T) { // Config: testAccRoleConfig_diffs(rName, ""), // PlanOnly: true, // }, - { - Config: testAccRoleConfig_diffs(rName, ""), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - ), - }, - { - Config: testAccRoleConfig_diffs(rName, ""), - PlanOnly: true, - }, // TODO: I don't think you can do this in plugin sdk, either nil or have something // Either way can be figured out later { @@ -659,24 +660,26 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &role), resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "2"), - // TODO: remove this once we add managed_policy_arns - // should be null? - // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + // TODO: remove this once we add managed_policy_arns + // should be null? + // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), ), }, // { // Config: testAccRoleConfig_policyInlineUpdateDown(rName, policyName3), // Check: resource.ComposeTestCheckFunc( // testAccCheckRoleExists(ctx, resourceName, &role), - // resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), - // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + // resource.TestCheckResourceAttr(resourceName, "inline_policy.%", "1"), + // // TODO: remove this once we add managed_policy_arns + // // should be null? + // // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), // ), // }, // { - // ResourceName: resourceName, - // ImportState: true, - // ImportStateVerify: true, - // ImportStateVerifyIgnore: []string{"inline_policy.0.policy"}, + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // // ImportStateVerifyIgnore: []string{"inline_policy.0.policy"}, // }, }, }) From ede4280220f400c58f9a20a8ce5442bcc291e93f Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 15:24:11 -0500 Subject: [PATCH 052/118] another part passing --- internal/service/iam/role_test.go | 34 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index d3f06f31c4c..0f39afda1fc 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -665,21 +665,21 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), ), }, + { + Config: testAccRoleConfig_policyInlineUpdateDown(rName, policyName3), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), + // TODO: remove this once we add managed_policy_arns + // should be null? + // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + ), + }, // { - // Config: testAccRoleConfig_policyInlineUpdateDown(rName, policyName3), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &role), - // resource.TestCheckResourceAttr(resourceName, "inline_policy.%", "1"), - // // TODO: remove this once we add managed_policy_arns - // // should be null? - // // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), - // ), - // }, - // { - // ResourceName: resourceName, - // ImportState: true, - // ImportStateVerify: true, - // // ImportStateVerifyIgnore: []string{"inline_policy.0.policy"}, + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{"inline_policies"}, // }, }, }) @@ -1906,10 +1906,8 @@ resource "aws_iam_role" "test" { }] }) - inline_policy { - name = %[2]q - - policy = < Date: Sun, 21 Jan 2024 15:33:14 -0500 Subject: [PATCH 053/118] finish inline basic --- internal/service/iam/role_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 0f39afda1fc..a04cb5ad60d 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -675,12 +675,12 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), ), }, - // { - // ResourceName: resourceName, - // ImportState: true, - // ImportStateVerify: true, - // ImportStateVerifyIgnore: []string{"inline_policies"}, - // }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{fmt.Sprintf("inline_policies.%s", policyName3)}, + }, }, }) } From 23f1db5c41aa8b570da4e0005942afa4f75eadf9 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 16:39:59 -0500 Subject: [PATCH 054/118] finish basic inline test --- internal/service/iam/role.go | 42 +++++----- internal/service/iam/role_test.go | 133 ++++++++++++++++-------------- 2 files changed, 92 insertions(+), 83 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 6ae1a94aca4..168966776e8 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -486,29 +486,33 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } state.AssumeRolePolicy = fwtypes.IAMPolicyValue(policyToSet) - inlinePolicies, err := r.readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName)) - if err != nil { - // TODO: figure out this error - return - // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) - } + // Unforunately because of `aws_iam_role_policy` and those like it, we have to ignore unless + // added via create + if !state.InlinePolicies.IsNull() && !state.InlinePolicies.IsUnknown() { + inlinePolicies, err := r.readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName)) + if err != nil { + // TODO: figure out this error + return + // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) + } + fmt.Println(fmt.Sprintf("inlinePolicies: %+v", inlinePolicies)) - var configPoliciesList []*iam.PutRolePolicyInput - if !state.InlinePolicies.IsNull() && !state.ARN.IsUnknown() { + var configPoliciesList []*iam.PutRolePolicyInput inline_policies_map := make(map[string]string) state.InlinePolicies.ElementsAs(ctx, &inline_policies_map, false) configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), inline_policies_map) - } - - if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { - fmt.Println("found different inline policies!") - state.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, flattenRoleInlinePolicies(inlinePolicies)) - // if err := d.Set("inline_policy", flattenRoleInlinePolicies(inlinePolicies)); err != nil { - // // TODO: make error here - // fmt.Printf("error flattening inline policies in read") - // return - // // return sdkdiag.AppendErrorf(diags, "setting inline_policy: %s", err) - // } + fmt.Println(fmt.Sprintf("configPoliciesList: %+v", configPoliciesList)) + + if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { + fmt.Println("found different inline policies!") + state.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, flattenRoleInlinePolicies(inlinePolicies)) + // if err := d.Set("inline_policy", flattenRoleInlinePolicies(inlinePolicies)); err != nil { + // // TODO: make error here + // fmt.Printf("error flattening inline policies in read") + // return + // // return sdkdiag.AppendErrorf(diags, "setting inline_policy: %s", err) + // } + } } // policyARNs, err := findRoleAttachedPolicies(ctx, conn, d.Id()) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index a04cb5ad60d..a9081700b4b 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -200,66 +200,66 @@ func TestAccIAMRole_diffsNoCondition(t *testing.T) { CheckDestroy: testAccCheckRoleDestroy(ctx), Steps: []resource.TestStep{ // TODO: have to validate this... - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &conf), - // ), - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // PlanOnly: true, - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &conf), - // ), - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // PlanOnly: true, - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &conf), - // ), - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // PlanOnly: true, - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &conf), - // ), - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // PlanOnly: true, - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &conf), - // ), - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // PlanOnly: true, - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &conf), - // ), - // }, - // { - // Config: testAccRoleConfig_diffs(rName, ""), - // PlanOnly: true, - // }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + ), + }, + { + Config: testAccRoleConfig_diffs(rName, ""), + PlanOnly: true, + }, // TODO: I don't think you can do this in plugin sdk, either nil or have something // Either way can be figured out later { @@ -676,10 +676,15 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{fmt.Sprintf("inline_policies.%s", policyName3)}, + // As stated in other comments, to allow other resources that + // attach inlinen policies we have to ignore inline_policies on import + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + fmt.Sprintf("inline_policies.%s", policyName3), + "inline_policies.%", + }, }, }, }) From c5249f508c9a62a331d6de617f954e261dacdacf Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 17:50:14 -0500 Subject: [PATCH 055/118] make comment about plan modifier test --- internal/service/iam/role.go | 2 +- internal/service/iam/role_test.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 168966776e8..1999465b059 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -126,6 +126,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "inline_policies": schema.MapAttribute{ ElementType: types.StringType, Optional: true, + // TODO: have to add plan modifier here to suppress func }, // "managed_policy_arns": schema.SetAttribute{ // Computed: true, @@ -153,7 +154,6 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest stringplanmodifier.RequiresReplaceIfConfigured(), stringplanmodifier.UseStateForUnknown(), }, - // Default: stringdefault.StaticString(""), Validators: []validator.String{ stringvalidator.LengthAtMost(roleNameMaxLen), // TODO: uncomment when ready diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index a9081700b4b..f64998e3b2a 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -690,6 +690,9 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { }) } +// TODO: have to do some plan modification for this, have to read up on the docs more +// Ideally only show diffs for each policy that actually changed + // // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19444 // func TestAccIAMRole_InlinePolicy_ignoreOrder(t *testing.T) { // ctx := acctest.Context(t) From dc1d2c0fd6db956e3eabef18d9a3b4ab33a2df85 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 17:56:19 -0500 Subject: [PATCH 056/118] empty inline policy test --- internal/service/iam/role_test.go | 42 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index f64998e3b2a..c6fb8bb3288 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -732,27 +732,27 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { // }) // } -// func TestAccIAMRole_InlinePolicy_empty(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_InlinePolicy_empty(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyEmptyInline(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyEmptyInline(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + ), + }, + }, + }) +} // func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { // ctx := acctest.Context(t) @@ -2423,7 +2423,7 @@ resource "aws_iam_role" "test" { }] }) - inline_policy {} + inline_policies = {} } `, roleName) } From 2820bff3cd1cfc1f11d0652497ebc7aa549aeca6 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 18:04:14 -0500 Subject: [PATCH 057/118] adding unique id and a test for it --- internal/service/iam/role.go | 13 +++++++++---- internal/service/iam/role_test.go | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 1999465b059..dcd6b1c9cee 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -199,9 +199,12 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest stringplanmodifier.UseStateForUnknown(), }, }, - // "unique_id": schema.StringAttribute{ - // Computed: true, - // }, + "unique_id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, names.AttrTags: tftags.TagsAttribute(), names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), }, @@ -223,10 +226,10 @@ type resourceIamRoleData struct { Tags types.Map `tfsdk:"tags"` TagsAll types.Map `tfsdk:"tags_all"` InlinePolicies types.Map `tfsdk:"inline_policies"` + UniqueId types.String `tfsdk:"unique_id"` // TODO: still have to think this one out // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` - // UniqueId types.String `tfsdk:"unique_id"` } func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -339,6 +342,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, plan.ID = flex.StringToFramework(ctx, output.Role.RoleName) plan.Name = flex.StringToFramework(ctx, output.Role.RoleName) plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(output.Role.RoleName))) + plan.UniqueId = flex.StringToFramework(ctx, output.Role.RoleId) // last steps? // TODO: do we need something?this? @@ -449,6 +453,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.Description = flex.StringToFramework(ctx, role.Description) state.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(role.RoleName))) state.MaxSessionDuration = flex.Int64ToFramework(ctx, role.MaxSessionDuration) + state.UniqueId = flex.StringToFramework(ctx, role.RoleId) if state.ForceDetachPolicies.IsNull() { // TODO: better way to do this that is more framework friendly? diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index c6fb8bb3288..7e186c2937f 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -40,6 +40,7 @@ func TestAccIAMRole_basic(t *testing.T) { testAccCheckRoleExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "path", "/"), resource.TestCheckResourceAttrSet(resourceName, "create_date"), + resource.TestCheckResourceAttrSet(resourceName, "unique_id"), ), }, { From 3bb172639833efd09953c1182733218d9381ce3a Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 20:10:45 -0500 Subject: [PATCH 058/118] first part of managed arns --- internal/service/iam/role.go | 87 +++++++++-------- internal/service/iam/role_test.go | 156 +++++++++++++++--------------- 2 files changed, 124 insertions(+), 119 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index dcd6b1c9cee..f6113fe165a 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -128,14 +128,13 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Optional: true, // TODO: have to add plan modifier here to suppress func }, - // "managed_policy_arns": schema.SetAttribute{ - // Computed: true, - // Optional: true, - // ElementType: types.StringType, - // // TODO: set validator for arn - // // TODO: validate all elements of set are valid arns - // // how to do this with helper lib terraform-plugin-framework-validators - // }, + "managed_policy_arns": schema.SetAttribute{ + // TODO: maybe use setof custom type with arn? + // Then don't need validator + Computed: true, + Optional: true, + ElementType: types.StringType, + }, "max_session_duration": schema.Int64Attribute{ Optional: true, Computed: true, @@ -223,13 +222,11 @@ type resourceIamRoleData struct { NamePrefix types.String `tfsdk:"name_prefix"` Path types.String `tfsdk:"path"` PermissionsBoundary fwtypes.ARN `tfsdk:"permissions_boundary"` - Tags types.Map `tfsdk:"tags"` - TagsAll types.Map `tfsdk:"tags_all"` InlinePolicies types.Map `tfsdk:"inline_policies"` UniqueId types.String `tfsdk:"unique_id"` - - // TODO: still have to think this one out - // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` + ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` } func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -306,16 +303,16 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } } - // if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { - // managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) - // if err := r.addRoleManagedPolicies(ctx, roleName, managedPolicies); err != nil { - // resp.Diagnostics.AddError( - // create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), - // err.Error(), - // ) - // return - // } - // } + if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { + managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) + if err := r.addRoleManagedPolicies(ctx, roleName, managedPolicies); err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + err.Error(), + ) + return + } + } // For partitions not supporting tag-on-create, attempt tag after create. if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { @@ -368,14 +365,12 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, hasInline = true } - // hasManaged := false - // if !state.ManagedPolicyArns.IsNull() && !state.ManagedPolicyArns.IsUnknown() { - // hasManaged = true - // } + hasManaged := false + if !state.ManagedPolicyArns.IsNull() && !state.ManagedPolicyArns.IsUnknown() { + hasManaged = true + } - // err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, hasManaged) - // TODO: should name be ID here? - err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, false) + err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, hasManaged) if err != nil { // TODO: do something like this to skip deletes on roles that are gone? @@ -467,9 +462,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.PermissionsBoundary = fwtypes.ARNNull() } - // TODO: have to add this and maybe a test to validate this is non empty in basic - // d.Set("unique_id", role.RoleId) - assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) if err != nil { // TODO: I don't this this is right error, should look more into it @@ -520,15 +512,15 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } } - // policyARNs, err := findRoleAttachedPolicies(ctx, conn, d.Id()) - // if err != nil { - // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) - // } - // d.Set("managed_policy_arns", policyARNs) + policyARNs, err := findRoleAttachedPolicies(ctx, conn, state.ID.ValueString()) + if err != nil { + // TODO: implement error + return + // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) + } + state.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) setTagsOut(ctx, role.Tags) - // state.Tags = flex.FlattenFrameworkStringValueMapLegacy(ctx, KeyValueTags(ctx, role.Tags).Map()) - // data.Tags = flex.FlattenFrameworkStringValueMapLegacy(ctx, tags.Map()) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } @@ -725,6 +717,21 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } + // if !plan.ManagedPolicyArns.Equal(state.ManagedPolicyArns) { + // o, n := d.GetChange("managed_policy_arns") + // os, ns := o.(*schema.Set), n.(*schema.Set) + // add, del := flex.ExpandStringSet(ns.Difference(os)), flex.ExpandStringValueSet(os.Difference(ns)) + + // // TODO: fix this, we are doing it again. Always add then delete to prevent issues + // if err := deleteRolePolicyAttachments(ctx, conn, d.Id(), del); err != nil { + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) + // } + + // if err := addRoleManagedPolicies(ctx, d.Id(), add, meta); err != nil { + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) + // } + // } + if !plan.TagsAll.Equal(state.TagsAll) { fmt.Println("Tags are not equal!") err := roleUpdateTags(ctx, conn, plan.ID.ValueString(), state.TagsAll, plan.TagsAll) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 7e186c2937f..d9c275c2577 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -261,10 +261,8 @@ func TestAccIAMRole_diffsNoCondition(t *testing.T) { Config: testAccRoleConfig_diffs(rName, ""), PlanOnly: true, }, - // TODO: I don't think you can do this in plugin sdk, either nil or have something - // Either way can be figured out later { - // TODO: empty maps are not the same in plugin framework vs sdkv2 + // NOTE: empty maps are not the same in plugin framework vs sdkv2 // would have to possibly resolve this in later PR or open to any ideas // For now if you want no tags would have to set as null // rest of below test changed to null from `{}` @@ -755,85 +753,85 @@ func TestAccIAMRole_InlinePolicy_empty(t *testing.T) { }) } -// func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyManaged(rName, policyName1), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "name", rName), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), -// ), -// }, -// { -// Config: testAccRoleConfig_policyManagedUpdate(rName, policyName1, policyName2, policyName3), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "2"), -// ), -// }, -// { -// Config: testAccRoleConfig_policyManagedUpdateDown(rName, policyName1, policyName2, policyName3), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), -// ), -// }, -// { -// ResourceName: resourceName, -// ImportState: true, -// ImportStateVerify: true, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyManaged(rName, policyName1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), + ), + }, + // { + // Config: testAccRoleConfig_policyManagedUpdate(rName, policyName1, policyName2, policyName3), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &role), + // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "2"), + // ), + // }, + // { + // Config: testAccRoleConfig_policyManagedUpdateDown(rName, policyName1, policyName2, policyName3), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckRoleExists(ctx, resourceName, &role), + // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), + // ), + // }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} -// // TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_managedNonEmpty: if a policy is detached -// // out of band, it should be reattached. -// func TestAccIAMRole_ManagedPolicy_outOfBandRemovalAddedBack(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +// TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_managedNonEmpty: if a policy is detached +// out of band, it should be reattached. +func TestAccIAMRole_ManagedPolicy_outOfBandRemovalAddedBack(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyManaged(rName, policyName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyDetachManagedPolicy(ctx, &role, policyName), -// ), -// ExpectNonEmptyPlan: true, -// }, -// { -// Config: testAccRoleConfig_policyManaged(rName, policyName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyManaged(rName, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyDetachManagedPolicy(ctx, &role, policyName), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccRoleConfig_policyManaged(rName, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), + ), + }, + }, + }) +} // // TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_inlineNonEmpty: if a policy is removed // // out of band, it should be recreated. From 2c33470308736ef9a567629b71c795f5e5856acc Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:02:29 -0500 Subject: [PATCH 059/118] finish managed policy baseline test --- internal/service/iam/role.go | 103 +++++++++++++++++++++++------- internal/service/iam/role_test.go | 37 ++++++----- 2 files changed, 102 insertions(+), 38 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index f6113fe165a..96f1d5f2925 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -25,6 +25,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -131,9 +132,11 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "managed_policy_arns": schema.SetAttribute{ // TODO: maybe use setof custom type with arn? // Then don't need validator - Computed: true, Optional: true, ElementType: types.StringType, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, }, "max_session_duration": schema.Int64Attribute{ Optional: true, @@ -304,7 +307,12 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { - managedPolicies := flex.ExpandFrameworkStringSet(ctx, plan.ManagedPolicyArns) + fmt.Println("Hitting here!") + var managedPolicies []string + resp.Diagnostics.Append(plan.ManagedPolicyArns.ElementsAs(ctx, &managedPolicies, false)...) + if resp.Diagnostics.HasError() { + return + } if err := r.addRoleManagedPolicies(ctx, roleName, managedPolicies); err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), @@ -512,13 +520,16 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } } - policyARNs, err := findRoleAttachedPolicies(ctx, conn, state.ID.ValueString()) - if err != nil { - // TODO: implement error - return - // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) + // like Inline policies, only reading if set in state already via updates, create, etc + if !state.ManagedPolicyArns.IsNull() && !state.ManagedPolicyArns.IsUnknown() { + policyARNs, err := findRoleAttachedPolicies(ctx, conn, state.ID.ValueString()) + if err != nil { + // TODO: implement error + return + // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) + } + state.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) } - state.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) setTagsOut(ctx, role.Tags) @@ -643,9 +654,11 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, fmt.Println("Found inline policies changes!") old_inline_policies_map := make(map[string]string) + // TODO: add wrapper to this state.InlinePolicies.ElementsAs(ctx, &old_inline_policies_map, false) new_inline_policies_map := make(map[string]string) + // TODO: add wrapper to this plan.InlinePolicies.ElementsAs(ctx, &new_inline_policies_map, false) fmt.Println(fmt.Sprintf("len old_inline_policies_map: %v", len(old_inline_policies_map))) @@ -717,20 +730,66 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } - // if !plan.ManagedPolicyArns.Equal(state.ManagedPolicyArns) { - // o, n := d.GetChange("managed_policy_arns") - // os, ns := o.(*schema.Set), n.(*schema.Set) - // add, del := flex.ExpandStringSet(ns.Difference(os)), flex.ExpandStringValueSet(os.Difference(ns)) + if !plan.ManagedPolicyArns.Equal(state.ManagedPolicyArns) { + var oldManagedARNs, newManagedARNs []string + resp.Diagnostics.Append(state.ManagedPolicyArns.ElementsAs(ctx, &oldManagedARNs, false)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(plan.ManagedPolicyArns.ElementsAs(ctx, &newManagedARNs, false)...) + if resp.Diagnostics.HasError() { + return + } + + fmt.Println(fmt.Sprintf("oldManagedARNs: %+v", oldManagedARNs)) + fmt.Println(fmt.Sprintf("newManagedARNs: %+v", newManagedARNs)) + + var add, del []string + + oldPolicyArnMap := make(map[string]int64) + for _, v := range oldManagedARNs { + fmt.Println(fmt.Sprintf("oldPolicyArnMap loop on %s", v)) + oldPolicyArnMap[v] = 0 + } + + for _, v := range newManagedARNs { + fmt.Println(fmt.Sprintf("newManagedARNs loop on %s", v)) + if _, ok := oldPolicyArnMap[v]; !ok { + fmt.Println(fmt.Sprintf("adding %s", v)) + add = append(add, v) + } + } + + newPolicyArnMap := make(map[string]int64) + for _, v := range newManagedARNs { + fmt.Println(fmt.Sprintf("newPolicyArnMap loop on %s", v)) + newPolicyArnMap[v] = 0 + } + + for _, v := range oldManagedARNs { + if _, ok := newPolicyArnMap[v]; !ok { + del = append(del, v) + } + } - // // TODO: fix this, we are doing it again. Always add then delete to prevent issues - // if err := deleteRolePolicyAttachments(ctx, conn, d.Id(), del); err != nil { - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) - // } + // fmt.Println(fmt.Sprintf("add: %+v", add)) + fmt.Println(fmt.Sprintf("del: %+v", del)) + fmt.Println(fmt.Sprintf("add: %+v", del)) - // if err := addRoleManagedPolicies(ctx, d.Id(), add, meta); err != nil { - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) - // } - // } + // // TODO: fix this, we are doing it again. Always add then delete to prevent issues + if err := r.addRoleManagedPolicies(ctx, state.ID.ValueString(), add); err != nil { + // TODO: implement error + return + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) + } + + if err := deleteRolePolicyAttachments(ctx, conn, state.ID.ValueString(), del); err != nil { + // TODO: implement error + return + // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) + } + } if !plan.TagsAll.Equal(state.TagsAll) { fmt.Println("Tags are not equal!") @@ -815,12 +874,12 @@ func retryCreateRole(ctx context.Context, conn *iam.IAM, input *iam.CreateRoleIn return output, err } -func (r resourceIamRole) addRoleManagedPolicies(ctx context.Context, roleName string, policies []*string) error { +func (r resourceIamRole) addRoleManagedPolicies(ctx context.Context, roleName string, policies []string) error { conn := r.Meta().IAMConn(ctx) var errs []error for _, arn := range policies { - if err := attachPolicyToRole(ctx, conn, roleName, aws.StringValue(arn)); err != nil { + if err := attachPolicyToRole(ctx, conn, roleName, arn); err != nil { errs = append(errs, err) } } diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index d9c275c2577..b0077e53878 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -758,8 +758,8 @@ func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { var role iam.Role rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - // policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - // policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName3 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_iam_role.test" resource.ParallelTest(t, resource.TestCase{ @@ -776,24 +776,29 @@ func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), ), }, - // { - // Config: testAccRoleConfig_policyManagedUpdate(rName, policyName1, policyName2, policyName3), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &role), - // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "2"), - // ), - // }, - // { - // Config: testAccRoleConfig_policyManagedUpdateDown(rName, policyName1, policyName2, policyName3), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckRoleExists(ctx, resourceName, &role), - // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), - // ), - // }, + { + Config: testAccRoleConfig_policyManagedUpdate(rName, policyName1, policyName2, policyName3), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "2"), + ), + }, + { + Config: testAccRoleConfig_policyManagedUpdateDown(rName, policyName1, policyName2, policyName3), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), + ), + }, + // NOTE: Can't import managed arns unless they are already in state because of things like { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "managed_policy_arns.0", + "managed_policy_arns.#", + }, }, }, }) From f42d87a8e8da20d1cd6b17e6cbcedb8b3ce7d5f7 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:07:53 -0500 Subject: [PATCH 060/118] refactor for id schema --- internal/service/iam/role.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 96f1d5f2925..56f43eeb231 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -78,15 +78,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest stringplanmodifier.UseStateForUnknown(), }, }, - // TODO: should this be this? - // "github.com/hashicorp/terraform-provider-aws/internal/framework" - //framework.IDAttribute() - "id": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, + "id": framework.IDAttribute(), "assume_role_policy": schema.StringAttribute{ Required: true, CustomType: fwtypes.IAMPolicyType, From 912a9e8e2d84c4e3877788fb1dbc9ca19713b631 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:45:41 -0500 Subject: [PATCH 061/118] add another inline policy test --- internal/service/iam/role_test.go | 64 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index b0077e53878..4ac15dfe92e 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -838,39 +838,39 @@ func TestAccIAMRole_ManagedPolicy_outOfBandRemovalAddedBack(t *testing.T) { }) } -// // TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_inlineNonEmpty: if a policy is removed -// // out of band, it should be recreated. -// func TestAccIAMRole_InlinePolicy_outOfBandRemovalAddedBack(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +// TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_inlineNonEmpty: if a policy is removed +// out of band, it should be recreated. +func TestAccIAMRole_InlinePolicy_outOfBandRemovalAddedBack(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyInline(rName, policyName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName), -// ), -// ExpectNonEmptyPlan: true, -// }, -// { -// Config: testAccRoleConfig_policyInline(rName, policyName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyInline(rName, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccRoleConfig_policyInline(rName, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), + ), + }, + }, + }) +} // // TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemoved: if managed_policy_arns arg // // exists and is non-empty, policy attached out of band should be removed From e634ef4fa49e55877bd25ed41beaad5dc3f900bf Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:49:31 -0500 Subject: [PATCH 062/118] another managed arn out of band policy test --- internal/service/iam/role_test.go | 62 +++++++++++++++---------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 4ac15dfe92e..a2d0033c80e 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -874,38 +874,38 @@ func TestAccIAMRole_InlinePolicy_outOfBandRemovalAddedBack(t *testing.T) { // // TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemoved: if managed_policy_arns arg // // exists and is non-empty, policy attached out of band should be removed -// func TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemoved(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemoved(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyExtraManaged(rName, policyName1, policyName2), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName2), -// ), -// ExpectNonEmptyPlan: true, -// }, -// { -// Config: testAccRoleConfig_policyExtraManaged(rName, policyName1, policyName2), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyExtraManaged(rName, policyName1, policyName2), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName2), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccRoleConfig_policyExtraManaged(rName, policyName1, policyName2), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), + ), + }, + }, + }) +} // // TestAccIAMRole_PolicyOutOfBandAdditionRemoved_inlineNonEmpty: if inline_policy arg // // exists and is non-empty, policy added out of band should be removed From 07fff5ce2861f675f2c2824cbbf7917d8ae182b4 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:55:28 -0500 Subject: [PATCH 063/118] another inline test --- internal/service/iam/role_test.go | 64 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index a2d0033c80e..1b0e6abdcda 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -909,39 +909,39 @@ func TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemoved(t *testing.T) { // // TestAccIAMRole_PolicyOutOfBandAdditionRemoved_inlineNonEmpty: if inline_policy arg // // exists and is non-empty, policy added out of band should be removed -// func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemoved(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemoved(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyInline(rName, policyName1), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName2), -// ), -// ExpectNonEmptyPlan: true, -// }, -// { -// Config: testAccRoleConfig_policyInline(rName, policyName1), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyInline(rName, policyName1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName2), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccRoleConfig_policyInline(rName, policyName1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + ), + }, + }, + }) +} // // TestAccIAMRole_PolicyOutOfBandAdditionIgnored_inlineNonExistent: if there is no // // inline_policy attribute, out of band changes should be ignored. From e2668bfbcbbe106661d13adee59934b42900c6a9 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:59:48 -0500 Subject: [PATCH 064/118] another test --- internal/service/iam/role_test.go | 80 +++++++++++++++---------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 1b0e6abdcda..7aa72b381ab 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -943,47 +943,47 @@ func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemoved(t *testing.T) { }) } -// // TestAccIAMRole_PolicyOutOfBandAdditionIgnored_inlineNonExistent: if there is no -// // inline_policy attribute, out of band changes should be ignored. -// func TestAccIAMRole_InlinePolicy_outOfBandAdditionIgnored(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +// TestAccIAMRole_PolicyOutOfBandAdditionIgnored_inlineNonExistent: if there is no +// inline_policy attribute, out of band changes should be ignored. +func TestAccIAMRole_InlinePolicy_outOfBandAdditionIgnored(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyNoInline(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName1), -// ), -// }, -// { -// Config: testAccRoleConfig_policyNoInline(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName2), -// ), -// }, -// { -// Config: testAccRoleConfig_policyNoInline(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName1), -// testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName2), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyNoInline(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName1), + ), + }, + { + Config: testAccRoleConfig_policyNoInline(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName2), + ), + }, + { + Config: testAccRoleConfig_policyNoInline(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName1), + testAccCheckRolePolicyRemoveInlinePolicy(ctx, &role, policyName2), + ), + }, + }, + }) +} // // TestAccIAMRole_PolicyOutOfBandAdditionIgnored_managedNonExistent: if there is no // // managed_policy_arns attribute, out of band changes should be ignored. From 7cae19f5c054a71d38ccf85c057f181b966e089b Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 00:05:22 -0500 Subject: [PATCH 065/118] another one --- internal/service/iam/role_test.go | 58 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 7aa72b381ab..13c1b3c2d19 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -987,36 +987,36 @@ func TestAccIAMRole_InlinePolicy_outOfBandAdditionIgnored(t *testing.T) { // // TestAccIAMRole_PolicyOutOfBandAdditionIgnored_managedNonExistent: if there is no // // managed_policy_arns attribute, out of band changes should be ignored. -// func TestAccIAMRole_ManagedPolicy_outOfBandAdditionIgnored(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_ManagedPolicy_outOfBandAdditionIgnored(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyNoManaged(rName, policyName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName), -// ), -// }, -// { -// Config: testAccRoleConfig_policyNoManaged(rName, policyName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyDetachManagedPolicy(ctx, &role, policyName), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyNoManaged(rName, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName), + ), + }, + { + Config: testAccRoleConfig_policyNoManaged(rName, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyDetachManagedPolicy(ctx, &role, policyName), + ), + }, + }, + }) +} // // TestAccIAMRole_PolicyOutOfBandAdditionRemoved_inlineEmpty: if inline is added // // out of band with empty inline arg, should be removed From 893df877df04bc4f5306259c57e8b568251887e0 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:33:36 -0500 Subject: [PATCH 066/118] another test --- internal/service/iam/role_test.go | 58 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 13c1b3c2d19..6e6a80f51f2 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -1020,36 +1020,36 @@ func TestAccIAMRole_ManagedPolicy_outOfBandAdditionIgnored(t *testing.T) { // // TestAccIAMRole_PolicyOutOfBandAdditionRemoved_inlineEmpty: if inline is added // // out of band with empty inline arg, should be removed -// func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemovedEmpty(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemovedEmpty(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyEmptyInline(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName), -// ), -// ExpectNonEmptyPlan: true, -// }, -// { -// Config: testAccRoleConfig_policyEmptyInline(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyEmptyInline(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyAddInlinePolicy(ctx, &role, policyName), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccRoleConfig_policyEmptyInline(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + ), + }, + }, + }) +} // // TestAccIAMRole_PolicyOutOfBandAdditionRemoved_managedEmpty: if managed is attached // // out of band with empty managed arg, should be detached From 597fe484b560c8d8618ba793f56a84d4905a5d68 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:53:14 -0500 Subject: [PATCH 067/118] another test --- internal/service/iam/role_test.go | 62 ++++++++++++++++--------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 6e6a80f51f2..f10ba5ca82c 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -1051,38 +1051,40 @@ func TestAccIAMRole_InlinePolicy_outOfBandAdditionRemovedEmpty(t *testing.T) { }) } +// NOTE: Cannot do this with terraform plugin sdk as previously +// default value of managed policy arn is null currently, so won't catch drift unless one already set // // TestAccIAMRole_PolicyOutOfBandAdditionRemoved_managedEmpty: if managed is attached // // out of band with empty managed arg, should be detached -// func TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemovedEmpty(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_ManagedPolicy_outOfBandAdditionRemovedEmpty(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyEmptyManaged(rName, policyName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName), -// ), -// ExpectNonEmptyPlan: true, -// }, -// { -// Config: testAccRoleConfig_policyEmptyManaged(rName, policyName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// ), -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyEmptyManaged(rName, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + testAccCheckRolePolicyAttachManagedPolicy(ctx, &role, policyName), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccRoleConfig_policyEmptyManaged(rName, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + ), + }, + }, + }) +} func testAccCheckRoleDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -2451,7 +2453,7 @@ resource "aws_iam_role" "test" { }] }) - managed_policy_arns = [] + managed_policy_arns = ["arn:aws:iam::aws:policy/AdministratorAccess-Amplify"] } resource "aws_iam_policy" "managed-policy1" { From b3551e5c52d20d342bc04fd7aad301ac4cfa23ae Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:07:53 -0500 Subject: [PATCH 068/118] small inline fix --- internal/service/iam/role.go | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 56f43eeb231..32a4d7b33c6 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -112,9 +112,6 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Optional: true, Computed: true, Default: booldefault.StaticBool(false), - // PlanModifiers: []planmodifier.Bool{ - // boolplanmodifier.UseStateForUnknown(), - // }, }, "inline_policies": schema.MapAttribute{ ElementType: types.StringType, @@ -281,14 +278,8 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, roleName := aws.StringValue(output.Role.RoleName) if !plan.InlinePolicies.IsNull() && !plan.InlinePolicies.IsUnknown() { - fmt.Println("Found Inline Policies!") - inline_policies_map := make(map[string]string) - plan.InlinePolicies.ElementsAs(ctx, &inline_policies_map, false) - // v, _ := plan.InlinePolicies.ToMapValue(ctx) - fmt.Println(fmt.Sprintf("len inline_policies_map: %v", len(inline_policies_map))) - // fmt.Println(fmt.Sprintf("inline_policies_map: %+v", inline_policies_map)) + inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) policies := expandRoleInlinePolicies(roleName, inline_policies_map) - // fmt.Println(fmt.Sprintf("policies: %+v", policies)) if err := r.addRoleInlinePolicies(ctx, policies); err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), From 8dcdddde67a6b3f01cbfeba92b4173d4adb24cab Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:33:00 -0500 Subject: [PATCH 069/118] fmt --- internal/service/iam/role.go | 39 +++++------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 32a4d7b33c6..d016a5fec49 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -486,8 +486,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res fmt.Println(fmt.Sprintf("inlinePolicies: %+v", inlinePolicies)) var configPoliciesList []*iam.PutRolePolicyInput - inline_policies_map := make(map[string]string) - state.InlinePolicies.ElementsAs(ctx, &inline_policies_map, false) + inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), inline_policies_map) fmt.Println(fmt.Sprintf("configPoliciesList: %+v", configPoliciesList)) @@ -636,68 +635,43 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, if !plan.InlinePolicies.Equal(state.InlinePolicies) && inlinePoliciesActualDiff(ctx, &plan, &state) { fmt.Println("Found inline policies changes!") - old_inline_policies_map := make(map[string]string) - // TODO: add wrapper to this - state.InlinePolicies.ElementsAs(ctx, &old_inline_policies_map, false) - - new_inline_policies_map := make(map[string]string) - // TODO: add wrapper to this - plan.InlinePolicies.ElementsAs(ctx, &new_inline_policies_map, false) - - fmt.Println(fmt.Sprintf("len old_inline_policies_map: %v", len(old_inline_policies_map))) - fmt.Println(fmt.Sprintf("len new_inline_policies_map: %v", len(new_inline_policies_map))) + old_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) + new_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) var remove_policy_names []string for k := range old_inline_policies_map { if _, ok := new_inline_policies_map[k]; !ok { - fmt.Println(fmt.Sprintf("Found inline policy to remove: %s", k)) remove_policy_names = append(remove_policy_names, k) } } // need set like object to store policy names we want to add add_policy_names := make(map[string]int64) - fmt.Println("Going into add_policy_names loop...") for k, v := range new_inline_policies_map { - fmt.Println(fmt.Sprintf("Looking at key: %s", k)) val, ok := old_inline_policies_map[k] // If the key exists if !ok { - fmt.Println(fmt.Sprintf("Found inline policy to add that does not exist: %s", k)) add_policy_names[k] = 0 continue } if equivalent, err := awspolicy.PoliciesAreEquivalent(aws.StringValue(&v), aws.StringValue(&val)); err != nil || !equivalent { - fmt.Println(fmt.Sprintf("Found inline policy to add that needs update: %s", k)) add_policy_names[k] = 0 } } - fmt.Println(fmt.Sprintf("len add_policy_names: %v", len(add_policy_names))) - fmt.Println(fmt.Sprintf("add_policy_names values: %+v", add_policy_names)) - fmt.Println(fmt.Sprintf("new_inline_policies_map: %+v", new_inline_policies_map)) - roleName := state.Name.ValueString() nsPolicies := expandRoleInlinePolicies(roleName, new_inline_policies_map) - fmt.Println(fmt.Sprintf("nsPolicies: %+v", nsPolicies)) // getting policy objects we want to add based on add_policy_names map var add_policies []*iam.PutRolePolicyInput - fmt.Println("Going into add_policies names loop...") for _, val := range nsPolicies { - fmt.Println(fmt.Sprintf("Looking at: %s", *val.PolicyName)) if _, ok := add_policy_names[*val.PolicyName]; ok { - fmt.Println("adding") add_policies = append(add_policies, val) } else { - fmt.Println("not found") } } - fmt.Println(fmt.Sprintf("len add_policies: %v", len(add_policies))) - fmt.Println(fmt.Sprintf("len remove_policy_names: %v", len(remove_policy_names))) - // Always add before delete if err := r.addRoleInlinePolicies(ctx, add_policies); err != nil { // TODO: add error here @@ -1118,11 +1092,8 @@ func (r resourceIamRole) addRoleInlinePolicies(ctx context.Context, policies []* func inlinePoliciesActualDiff(ctx context.Context, plan *resourceIamRoleData, state *resourceIamRoleData) bool { roleName := state.Name.ValueString() - old_inline_policies_map := make(map[string]string) - state.InlinePolicies.ElementsAs(ctx, &old_inline_policies_map, false) - - new_inline_policies_map := make(map[string]string) - plan.InlinePolicies.ElementsAs(ctx, &new_inline_policies_map, false) + old_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) + new_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) osPolicies := expandRoleInlinePolicies(roleName, old_inline_policies_map) nsPolicies := expandRoleInlinePolicies(roleName, new_inline_policies_map) From 2f368c861f871d18edb2ba0e49b2162d33890110 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 20:36:38 -0500 Subject: [PATCH 070/118] more cleanup changes --- internal/service/iam/role.go | 44 ++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index d016a5fec49..77db7712acb 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -82,10 +82,13 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "assume_role_policy": schema.StringAttribute{ Required: true, CustomType: fwtypes.IAMPolicyType, - // Validators: []validator.String{ - // // TODO: json validator + // TODO: possible plan validator? Or normalize what is going into state + // DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, + // DiffSuppressOnRefresh: true, + // StateFunc: func(v interface{}) string { + // json, _ := structure.NormalizeJsonString(v) + // return json // }, - // TODO: finish this, it get complicated }, "create_date": schema.StringAttribute{ Computed: true, @@ -99,14 +102,12 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Default: stringdefault.StaticString(""), Validators: []validator.String{ stringvalidator.LengthBetween(0, 1000), - // TODO: figure this out later for both validators - // stringvalidator.RegexMatches( - // regexache.MustCompile( - // `[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), - // `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`, - // ), }, - // TODO: do something here + // TODO: need to add validators and test + // ValidateFunc: validation.All( + // validation.StringDoesNotMatch(regexache.MustCompile("[“‘]"), "cannot contain specially formatted single or double quotes: [“‘]"), + // validation.StringMatch(regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`), + // ), }, "force_detach_policies": schema.BoolAttribute{ Optional: true, @@ -114,9 +115,28 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Default: booldefault.StaticBool(false), }, "inline_policies": schema.MapAttribute{ - ElementType: types.StringType, + ElementType: fwtypes.IAMPolicyType, Optional: true, - // TODO: have to add plan modifier here to suppress func + // TODO: validators and name func for both + // "name": { + // Type: schema.TypeString, + // Optional: true, // semantically required but syntactically optional to allow empty inline_policy + // ValidateFunc: validation.All( + // validation.StringIsNotEmpty, + // validRolePolicyName, + // ), + // }, + // "policy": { + // Type: schema.TypeString, + // Optional: true, // semantically required but syntactically optional to allow empty inline_policy + // ValidateFunc: verify.ValidIAMPolicyJSON, + // DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, + // DiffSuppressOnRefresh: true, + // StateFunc: func(v interface{}) string { + // json, _ := verify.LegacyPolicyNormalize(v) + // return json + // }, + // }, }, "managed_policy_arns": schema.SetAttribute{ // TODO: maybe use setof custom type with arn? From 5fba9f77ce79ad509405870aa44ecd11680b896f Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:03:40 -0500 Subject: [PATCH 071/118] cleanup more errors --- internal/service/iam/role.go | 105 ++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 77db7712acb..6a8d124388e 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -114,8 +114,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Computed: true, Default: booldefault.StaticBool(false), }, + // TODO: maybe mapof of IAMPolicytype? "inline_policies": schema.MapAttribute{ - ElementType: fwtypes.IAMPolicyType, + ElementType: types.StringType, Optional: true, // TODO: validators and name func for both // "name": { @@ -344,7 +345,6 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } } - // TODO: do I have to do this? should look at other resources plan.ARN = fwtypes.ARNValue(*output.Role.Arn) plan.CreateDate = flex.StringValueToFramework(ctx, output.Role.CreateDate.Format(time.RFC3339)) plan.ID = flex.StringToFramework(ctx, output.Role.RoleName) @@ -353,11 +353,6 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, plan.UniqueId = flex.StringToFramework(ctx, output.Role.RoleId) // last steps? - // TODO: do we need something?this? - // state.refreshFromOutput(ctx, out) - // fmt.Println(plan.ARN) - // fmt.Println(plan.ID) - // fmt.Println(plan.Name) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) fmt.Println("Bottom of Create") } @@ -442,8 +437,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res // occasionally, immediately after a role is created, AWS will give an ARN like AROAQ7SSZBKHREXAMPLE (unique ID) if role, err = waitRoleARNIsNotUniqueID(ctx, conn, state.ARN.ValueString(), role); err != nil { - // TODO: have to update this error - // return sdkdiag.AppendErrorf(diags, "reading IAM Role (%s): waiting for valid ARN: %s", d.Id(), err) resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionSetting, state.Name.String(), state.ARN.String(), err), err.Error(), @@ -475,7 +468,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res assumeRolePolicy, err := url.QueryUnescape(aws.StringValue(role.AssumeRolePolicyDocument)) if err != nil { - // TODO: I don't this this is right error, should look more into it resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionReading, state.ID.String(), state.AssumeRolePolicy.String(), err), err.Error(), @@ -485,7 +477,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res policyToSet, err := verify.PolicyToSet(state.AssumeRolePolicy.ValueString(), assumeRolePolicy) if err != nil { - // TODO: I don't this this is right error, should look more into it resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionReading, state.ID.String(), state.AssumeRolePolicy.String(), err), err.Error(), @@ -499,11 +490,12 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res if !state.InlinePolicies.IsNull() && !state.InlinePolicies.IsUnknown() { inlinePolicies, err := r.readRoleInlinePolicies(ctx, aws.StringValue(role.RoleName)) if err != nil { - // TODO: figure out this error + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionReading, state.InlinePolicies.String(), state.ID.String(), err), + err.Error(), + ) return - // return sdkdiag.AppendErrorf(diags, "reading inline policies for IAM role %s, error: %s", d.Id(), err) } - fmt.Println(fmt.Sprintf("inlinePolicies: %+v", inlinePolicies)) var configPoliciesList []*iam.PutRolePolicyInput inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) @@ -513,12 +505,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { fmt.Println("found different inline policies!") state.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, flattenRoleInlinePolicies(inlinePolicies)) - // if err := d.Set("inline_policy", flattenRoleInlinePolicies(inlinePolicies)); err != nil { - // // TODO: make error here - // fmt.Printf("error flattening inline policies in read") - // return - // // return sdkdiag.AppendErrorf(diags, "setting inline_policy: %s", err) - // } } } @@ -526,9 +512,11 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res if !state.ManagedPolicyArns.IsNull() && !state.ManagedPolicyArns.IsUnknown() { policyARNs, err := findRoleAttachedPolicies(ctx, conn, state.ID.ValueString()) if err != nil { - // TODO: implement error + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionReading, state.ManagedPolicyArns.String(), state.ID.String(), err), + err.Error(), + ) return - // return sdkdiag.AppendErrorf(diags, "reading IAM Policies attached to Role (%s): %s", d.Id(), err) } state.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) } @@ -552,8 +540,10 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, if !plan.AssumeRolePolicy.Equal(state.AssumeRolePolicy) { assumeRolePolicy, err := structure.NormalizeJsonString(plan.AssumeRolePolicy.ValueString()) if err != nil { - // TODO: update error here - // return sdkdiag.AppendErrorf(diags, "assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.AssumeRolePolicy.String(), plan.ID.String(), err), + err.Error(), + ) return } @@ -576,8 +566,10 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, ) if err != nil { - // TODO: update error - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) assume role policy: %s", d.Id(), err) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.AssumeRolePolicy.String(), state.ID.String(), err), + err.Error(), + ) return } } @@ -591,9 +583,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, _, err := conn.UpdateRoleDescriptionWithContext(ctx, input) if err != nil { - // TODO: put something there - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) description: %s", d.Id(), err) - fmt.Println("Error updating description") resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionReading, state.ID.String(), plan.Description.String(), err), err.Error(), @@ -613,10 +602,11 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, _, err := conn.UpdateRoleWithContext(ctx, input) if err != nil { - // TODO: add error here - fmt.Println("Hit update max session duration error") + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.MaxSessionDuration.String(), err), + err.Error(), + ) return - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) MaxSessionDuration: %s", d.Id(), err) } state.MaxSessionDuration = plan.MaxSessionDuration } @@ -631,9 +621,11 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, _, err := conn.PutRolePermissionsBoundaryWithContext(ctx, input) if err != nil { - // TODO: implement this error + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.PermissionsBoundary.String(), err), + err.Error(), + ) return - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s) permissions boundary: %s", d.Id(), err) } } else { input := &iam.DeleteRolePermissionsBoundaryInput{ @@ -643,9 +635,11 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, _, err := conn.DeleteRolePermissionsBoundaryWithContext(ctx, input) if err != nil { - // TODO: implement error + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionDeleting, state.ID.String(), plan.PermissionsBoundary.String(), err), + err.Error(), + ) return - // return sdkdiag.AppendErrorf(diags, "deleting IAM Role (%s) permissions boundary: %s", d.Id(), err) } } @@ -694,15 +688,19 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, // Always add before delete if err := r.addRoleInlinePolicies(ctx, add_policies); err != nil { - // TODO: add error here + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.InlinePolicies.String(), err), + err.Error(), + ) return - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) } if err := deleteRoleInlinePolicies(ctx, conn, roleName, remove_policy_names); err != nil { - // TODO: add error here + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.InlinePolicies.String(), err), + err.Error(), + ) return - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) } } @@ -754,17 +752,20 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, fmt.Println(fmt.Sprintf("del: %+v", del)) fmt.Println(fmt.Sprintf("add: %+v", del)) - // // TODO: fix this, we are doing it again. Always add then delete to prevent issues if err := r.addRoleManagedPolicies(ctx, state.ID.ValueString(), add); err != nil { - // TODO: implement error + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.ManagedPolicyArns.String(), err), + err.Error(), + ) return - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) } if err := deleteRolePolicyAttachments(ctx, conn, state.ID.ValueString(), del); err != nil { - // TODO: implement error + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.ManagedPolicyArns.String(), err), + err.Error(), + ) return - // return sdkdiag.AppendErrorf(diags, "updating IAM Role (%s): %s", d.Id(), err) } } @@ -774,16 +775,18 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, // Some partitions (e.g. ISO) may not support tagging. if errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { - // TODO: implement error here - fmt.Println("Hit error parition updating!") + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.TagsAll.String(), err), + err.Error(), + ) return - // return append(diags, resourceRoleRead(ctx, d, meta)...) } if err != nil { - fmt.Println("Hit error updating!") - // TODO: implement error here - // return sdkdiag.AppendErrorf(diags, "updating tags for IAM Role (%s): %s", d.Id(), err) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.TagsAll.String(), err), + err.Error(), + ) return } } else { From e7f7aaee171c71cdfd56815a2363c5f20eb01586 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 22:08:22 -0500 Subject: [PATCH 072/118] move managed policies to arn type for set --- internal/service/iam/role.go | 6 ++--- internal/service/iam/role_test.go | 41 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 6a8d124388e..a9ce88225e1 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -140,10 +140,8 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // }, }, "managed_policy_arns": schema.SetAttribute{ - // TODO: maybe use setof custom type with arn? - // Then don't need validator - Optional: true, - ElementType: types.StringType, + Optional: true, + ElementType: fwtypes.ARNType, PlanModifiers: []planmodifier.Set{ setplanmodifier.UseStateForUnknown(), }, diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index f10ba5ca82c..90cd8bd5cd2 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -804,6 +804,24 @@ func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { }) } +func TestAccIAMRole_ManagedPolicy_badARN(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyManaged_badARN(rName), + ExpectError: regexache.MustCompile(`.*ARN Type Validation Error*`), + }, + }, + }) +} + // TestAccIAMRole_PolicyOutOfBandRemovalAddedBack_managedNonEmpty: if a policy is detached // out of band, it should be reattached. func TestAccIAMRole_ManagedPolicy_outOfBandRemovalAddedBack(t *testing.T) { @@ -2059,6 +2077,29 @@ resource "aws_iam_role" "test" { `, roleName) } +func testAccRoleConfig_policyManaged_badARN(roleName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + managed_policy_arns = ["iamabadarn"] + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Action = "sts:AssumeRole", + Principal = { + Service = "ec2.${data.aws_partition.current.dns_suffix}", + } + Effect = "Allow" + Sid = "" + }] + }) +} +`, roleName) +} + func testAccRoleConfig_policyManaged(roleName, policyName string) string { return fmt.Sprintf(` data "aws_partition" "current" {} From ba1e8c8acc8412030fad0a10a887daf9128dc7c1 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 22:32:49 -0500 Subject: [PATCH 073/118] fmt --- internal/service/iam/role.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index a9ce88225e1..3ac2378baa6 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -140,7 +140,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // }, }, "managed_policy_arns": schema.SetAttribute{ - Optional: true, + Optional: true, ElementType: fwtypes.ARNType, PlanModifiers: []planmodifier.Set{ setplanmodifier.UseStateForUnknown(), From dc48303d96e2fe4d9889bb6d05836987861662a8 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 22 Jan 2024 22:40:34 -0500 Subject: [PATCH 074/118] another improvement wtih validation on iam policies for inline policies with test --- internal/service/iam/role.go | 3 +- internal/service/iam/role_test.go | 58 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 3ac2378baa6..0456b896482 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -116,7 +116,8 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, // TODO: maybe mapof of IAMPolicytype? "inline_policies": schema.MapAttribute{ - ElementType: types.StringType, + // ElementType: types.StringType, + ElementType: fwtypes.IAMPolicyType, Optional: true, // TODO: validators and name func for both // "name": { diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 90cd8bd5cd2..7514ade5bcf 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -689,6 +689,24 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { }) } +func TestAccIAMRole_InlinePolicy_badJSON(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyInline_badJSON(rName), + ExpectError: regexache.MustCompile(`.*Invalid JSON String Value*`), + }, + }, + }) +} + // TODO: have to do some plan modification for this, have to read up on the docs more // Ideally only show diffs for each policy that actually changed @@ -1810,6 +1828,46 @@ resource "aws_iam_role" "test" { `, rName) } +func testAccRoleConfig_policyInline_badJSON(roleName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Action = "sts:AssumeRole", + Principal = { + Service = "ec2.${data.aws_partition.current.dns_suffix}", + } + Effect = "Allow" + Sid = "" + }] + }) + + inline_policies = { + "validName" = < Date: Mon, 22 Jan 2024 23:23:51 -0500 Subject: [PATCH 075/118] add minor comment about name validation for inline policies --- internal/service/iam/role.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 0456b896482..bf1c1bc2e1f 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -119,6 +119,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // ElementType: types.StringType, ElementType: fwtypes.IAMPolicyType, Optional: true, + // TODO: custom validator for name stuff? // TODO: validators and name func for both // "name": { // Type: schema.TypeString, From b7ef39ed7baf108f8c02f9ba609d02f96ac291d2 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:17:57 -0500 Subject: [PATCH 076/118] minor fix for inline policy comparison --- internal/service/iam/role.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index bf1c1bc2e1f..857c3ae0dab 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -669,7 +669,7 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, continue } - if equivalent, err := awspolicy.PoliciesAreEquivalent(aws.StringValue(&v), aws.StringValue(&val)); err != nil || !equivalent { + if !verify.PolicyStringsEquivalent(v, val) { add_policy_names[k] = 0 } } From 7d6704e83c5836d2ee0ca5b893f2478a9ccb5034 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Wed, 24 Jan 2024 20:38:41 -0500 Subject: [PATCH 077/118] work with getting inline policy plans clean --- internal/service/iam/role.go | 76 ++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 857c3ae0dab..78392f11be9 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -67,6 +67,60 @@ func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataR response.TypeName = "aws_iam_role" } +func EditPlanForSameReorderedPolicies() planmodifier.Map { + return editPlanForSameReorderedPolicies{} +} + +// editPlanForSameReorderedPolicies implements the plan modifier. +type editPlanForSameReorderedPolicies struct{} + +// Description returns a human-readable description of the plan modifier. +// TODO: edit this once we get working +func (m editPlanForSameReorderedPolicies) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +// TODO: edit this once we get working +func (m editPlanForSameReorderedPolicies) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// TODO: move to modify plan?? +func (m editPlanForSameReorderedPolicies) PlanModifyMap(ctx context.Context, req planmodifier.MapRequest, resp *planmodifier.MapResponse) { + // Do nothing if there is no state value. + if req.PlanValue.IsUnknown() || req.PlanValue.IsNull() { + return + } + + // TODO: something with making sure not just unknown but has value + if req.StateValue.IsUnknown() || req.StateValue.IsNull() { + return + } + + // TODO: do this more official way? + plan_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, req.PlanValue) + + if len(plan_inline_policies_map) == 0 { + // fmt.Println("empty plan map exiting") + return + } + state_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, req.StateValue) + // fmt.Println(fmt.Sprintf("state_inline_policies_map: %+v", state_inline_policies_map)) + // fmt.Println(fmt.Sprintf("plan_inline_policies_map: %+v", plan_inline_policies_map)) + + // If policies match, set plan for policy to use state version so that we don't see if diff bc ordering does not matter + for name, plan_policy_doc := range plan_inline_policies_map { + if state_policy_doc, ok := state_inline_policies_map[name]; ok { + if verify.PolicyStringsEquivalent(plan_policy_doc, state_policy_doc) { + plan_inline_policies_map[name] = state_policy_doc + } + } + } + + resp.PlanValue = flex.FlattenFrameworkStringValueMap(ctx, plan_inline_policies_map) +} + func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ @@ -119,6 +173,10 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // ElementType: types.StringType, ElementType: fwtypes.IAMPolicyType, Optional: true, + PlanModifiers: []planmodifier.Map{ + EditPlanForSameReorderedPolicies(), + // TODO: custom plan modifier for something like editing plan is fine + }, // TODO: custom validator for name stuff? // TODO: validators and name func for both // "name": { @@ -179,7 +237,6 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplaceIfConfigured(), - stringplanmodifier.UseStateForUnknown(), }, Validators: []validator.String{ stringvalidator.LengthAtMost(roleNamePrefixMaxLen), @@ -311,7 +368,6 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } if !plan.ManagedPolicyArns.IsNull() && !plan.ManagedPolicyArns.IsUnknown() { - fmt.Println("Hitting here!") var managedPolicies []string resp.Diagnostics.Append(plan.ManagedPolicyArns.ElementsAs(ctx, &managedPolicies, false)...) if resp.Diagnostics.HasError() { @@ -524,6 +580,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res setTagsOut(ctx, role.Tags) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + fmt.Println("Bottom of Read") } func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { @@ -717,28 +774,21 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, return } - fmt.Println(fmt.Sprintf("oldManagedARNs: %+v", oldManagedARNs)) - fmt.Println(fmt.Sprintf("newManagedARNs: %+v", newManagedARNs)) - var add, del []string oldPolicyArnMap := make(map[string]int64) for _, v := range oldManagedARNs { - fmt.Println(fmt.Sprintf("oldPolicyArnMap loop on %s", v)) oldPolicyArnMap[v] = 0 } for _, v := range newManagedARNs { - fmt.Println(fmt.Sprintf("newManagedARNs loop on %s", v)) if _, ok := oldPolicyArnMap[v]; !ok { - fmt.Println(fmt.Sprintf("adding %s", v)) add = append(add, v) } } newPolicyArnMap := make(map[string]int64) for _, v := range newManagedARNs { - fmt.Println(fmt.Sprintf("newPolicyArnMap loop on %s", v)) newPolicyArnMap[v] = 0 } @@ -748,10 +798,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } } - // fmt.Println(fmt.Sprintf("add: %+v", add)) - fmt.Println(fmt.Sprintf("del: %+v", del)) - fmt.Println(fmt.Sprintf("add: %+v", del)) - if err := r.addRoleManagedPolicies(ctx, state.ID.ValueString(), add); err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.ManagedPolicyArns.String(), err), @@ -770,7 +816,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } if !plan.TagsAll.Equal(state.TagsAll) { - fmt.Println("Tags are not equal!") err := roleUpdateTags(ctx, conn, plan.ID.ValueString(), state.TagsAll, plan.TagsAll) // Some partitions (e.g. ISO) may not support tagging. @@ -789,10 +834,7 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, ) return } - } else { - fmt.Println("Tags are equal") } - plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(plan.Name.ValueString())) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) From 27b67e677d18bc2c28512dc58cce93521950d5ca Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Wed, 24 Jan 2024 20:58:23 -0500 Subject: [PATCH 078/118] test helper function changes for ignoreOrder stuff --- internal/service/iam/role_test.go | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 7514ade5bcf..0e713b34e75 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -727,7 +727,7 @@ func TestAccIAMRole_InlinePolicy_badJSON(t *testing.T) { // Config: testAccRoleConfig_policyInlineActionOrder(rName), // Check: resource.ComposeTestCheckFunc( // testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), +// resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), // resource.TestCheckResourceAttr(resourceName, "name", rName), // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), // ), @@ -2035,10 +2035,8 @@ resource "aws_iam_role" "test" { }] }) - inline_policy { - name = %[1]q - - policy = jsonencode({ + inline_policies = { + %[1]q = jsonencode({ Version = "2012-10-17" Statement = [{ Action = [ @@ -2075,10 +2073,8 @@ resource "aws_iam_role" "test" { }] }) - inline_policy { - name = %[1]q - - policy = jsonencode({ + inline_policies = { + %[1]q = jsonencode({ Version = "2012-10-17" Statement = [{ Action = [ @@ -2114,10 +2110,8 @@ resource "aws_iam_role" "test" { }] }) - inline_policy { - name = %[1]q - - policy = jsonencode({ + inline_policies = { + %[1]q = jsonencode({ Version = "2012-10-17" Statement = [{ Action = [ From bcc59ab2528f89244a5a8b0fab960848694086d8 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:26:58 -0500 Subject: [PATCH 079/118] add comment about failing test case --- internal/service/iam/role_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 0e713b34e75..bb0872d8a0c 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -707,8 +707,7 @@ func TestAccIAMRole_InlinePolicy_badJSON(t *testing.T) { }) } -// TODO: have to do some plan modification for this, have to read up on the docs more -// Ideally only show diffs for each policy that actually changed +// TODO: So close on this, for some reason namePrefix is showing up when Plan is modified... very annoying // // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19444 // func TestAccIAMRole_InlinePolicy_ignoreOrder(t *testing.T) { From 64595b160da6cdb54acff5f6e2eac87cbaf0503f Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:37:50 -0500 Subject: [PATCH 080/118] Got failing basic migration test working --- internal/service/iam/role_test.go | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index bb0872d8a0c..50bbb90f1b3 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -41,6 +41,7 @@ func TestAccIAMRole_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "path", "/"), resource.TestCheckResourceAttrSet(resourceName, "create_date"), resource.TestCheckResourceAttrSet(resourceName, "unique_id"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), ), }, { @@ -52,6 +53,42 @@ func TestAccIAMRole_basic(t *testing.T) { }) } +func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.33.0", + }, + }, + Config: testAccRoleConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttrSet(resourceName, "create_date"), + resource.TestCheckResourceAttrSet(resourceName, "unique_id"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccRoleConfig_basic(rName), + PlanOnly: true, + }, + }, + }) +} + func TestAccIAMRole_description(t *testing.T) { ctx := acctest.Context(t) var conf iam.Role From adc7593fe33be0482750e0c0bf7acdb1842373ab Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Thu, 25 Jan 2024 20:43:33 -0500 Subject: [PATCH 081/118] work so far to get upgrade working, getting there but made good progress --- internal/service/iam/role.go | 215 +++++++++++++++++++++++++++++++++-- 1 file changed, 208 insertions(+), 7 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 78392f11be9..7a015ab77eb 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -123,6 +123,7 @@ func (m editPlanForSameReorderedPolicies) PlanModifyMap(ctx context.Context, req func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ + Version: 1, Attributes: map[string]schema.Attribute{ "arn": schema.StringAttribute{ CustomType: fwtypes.ARNType, @@ -173,10 +174,10 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // ElementType: types.StringType, ElementType: fwtypes.IAMPolicyType, Optional: true, - PlanModifiers: []planmodifier.Map{ - EditPlanForSameReorderedPolicies(), - // TODO: custom plan modifier for something like editing plan is fine - }, + // PlanModifiers: []planmodifier.Map{ + // EditPlanForSameReorderedPolicies(), + // // TODO: custom plan modifier for something like editing plan is fine + // }, // TODO: custom validator for name stuff? // TODO: validators and name func for both // "name": { @@ -293,12 +294,211 @@ type resourceIamRoleData struct { Path types.String `tfsdk:"path"` PermissionsBoundary fwtypes.ARN `tfsdk:"permissions_boundary"` InlinePolicies types.Map `tfsdk:"inline_policies"` - UniqueId types.String `tfsdk:"unique_id"` + UniqueID types.String `tfsdk:"unique_id"` ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` Tags types.Map `tfsdk:"tags"` TagsAll types.Map `tfsdk:"tags_all"` } +func oldSDKIAMRoleSchema(ctx context.Context) schema.Schema { + return schema.Schema{ + Version: 0, + Attributes: map[string]schema.Attribute{ + "arn": schema.StringAttribute{ + Computed: true, + }, + "assume_role_policy": schema.StringAttribute{ + Required: true, + // TODO Validate, + }, + "create_date": schema.StringAttribute{ + Computed: true, + }, + "description": schema.StringAttribute{ + Optional: true, + // TODO Validate, + }, + "force_detach_policies": schema.BoolAttribute{ + Optional: true, + // TODO Default:false, + }, + "id": // TODO framework.IDAttribute() + schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "managed_policy_arns": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + Computed: true, + }, + "max_session_duration": schema.Int64Attribute{ + Optional: true, + // TODO Default:3600, + // TODO Validate, + }, + "name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + // TODO Validate, + }, + "name_prefix": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + // TODO Validate, + }, + "path": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString("/"), + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "permissions_boundary": schema.StringAttribute{ + Optional: true, + // TODO Validate, + }, + "tags": // TODO tftags.TagsAttribute() + schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + }, + "tags_all": // TODO tftags.TagsAttributeComputedOnly() + schema.MapAttribute{ + ElementType: types.StringType, + Optional: true, + Computed: true, + }, + "unique_id": schema.StringAttribute{ + Computed: true, + }, + }, + Blocks: map[string]schema.Block{ + "inline_policy": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Optional: true, + // TODO Validate, + }, + "policy": schema.StringAttribute{ + Optional: true, + // TODO Validate, + }, + }, + }, + }, + }, + } +} + +func (r *resourceIamRole) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { + schemaV0 := oldSDKIAMRoleSchema(ctx) + + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: &schemaV0, + StateUpgrader: upgradeIAMRoleResourceStateV0toV1, + }, + } +} + +// TODO: ok finish working on this to perform upgrade cleanly +func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + fmt.Println("Top of upgrade") + type resourceIamRoleDataV0 struct { + ARN types.String `tfsdk:"arn"` + AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` + CreateDate types.String `tfsdk:"create_date"` + Description types.String `tfsdk:"description"` + ForceDetachPolicies types.Bool `tfsdk:"force_detach_policies"` + ID types.String `tfsdk:"id"` + ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` + MaxSessionDuration types.Int64 `tfsdk:"max_session_duration"` + Name types.String `tfsdk:"name"` + NamePrefix types.String `tfsdk:"name_prefix"` + Path types.String `tfsdk:"path"` + PermissionsBoundary types.String `tfsdk:"permissions_boundary"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` + UniqueID types.String `tfsdk:"unique_id"` + InlinePolicy types.Set `tfsdk:"inline_policy"` + } + + var roleDataV0 resourceIamRoleDataV0 + + resp.Diagnostics.Append(req.State.Get(ctx, &roleDataV0)...) + if resp.Diagnostics.HasError() { + fmt.Println("There was an error :(") + return + } + + fmt.Println("Made it here!") + + roleDataCurrent := resourceIamRoleData{ + ARN: fwtypes.ARNValue(roleDataV0.ARN.ValueString()), + AssumeRolePolicy: fwtypes.IAMPolicyValue(roleDataV0.AssumeRolePolicy.ValueString()), + CreateDate: roleDataV0.CreateDate, + Description: roleDataV0.Description, + ForceDetachPolicies: roleDataV0.ForceDetachPolicies, + ID: roleDataV0.ID, + MaxSessionDuration: roleDataV0.MaxSessionDuration, + Name: roleDataV0.Name, + Path: roleDataV0.Path, + UniqueID: roleDataV0.UniqueID, + // NamePrefix: roleDataV0.NamePrefix, + Tags: roleDataV0.Tags, + TagsAll: roleDataV0.TagsAll, + // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` + // InlinePolicy types.Set `tfsdk:"inline_policy"` + } + + // TODO: fix this later? + roleDataCurrent.NamePrefix = types.StringNull() + + // if roleDataV0.NamePrefix.ValueString() == "" { + // roleDataCurrent.NamePrefix = types.StringNull() + // // fmt.Println("Name prefix is empty!") + // } + + // TODO: do something with this once I get to that test + var policyARNs []string + roleDataCurrent.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) + + // TODO: do something with this once I get to that test + temp := make(map[string]string) + roleDataCurrent.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, temp) + + // TODO: update this to be string is empty check? + // if !roleDataV0.PermissionsBoundary.IsNull() { + // fmt.Println("permission boundary found") + // roleDataCurrent.PermissionsBoundary = fwtypes.ARNValue(roleDataV0.PermissionsBoundary.ValueString()) + // } + + // var managedPolicies []string + // resp.Diagnostics.Append(plan.ManagedPolicyArns.ElementsAs(ctx, &managedPolicies, false)...) + // if resp.Diagnostics.HasError() { + // return + // } + + // if jobQueueDataV0.SchedulingPolicyARN.ValueString() == "" { + // jobQueueDataV2.SchedulingPolicyARN = fwtypes.ARNNull() + // } + + diags := resp.State.Set(ctx, roleDataCurrent) + resp.Diagnostics.Append(diags...) + fmt.Println("Bottom of upgrade") +} + func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { fmt.Println("Hitting top of Create") conn := r.Meta().IAMConn(ctx) @@ -406,7 +606,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, plan.ID = flex.StringToFramework(ctx, output.Role.RoleName) plan.Name = flex.StringToFramework(ctx, output.Role.RoleName) plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(output.Role.RoleName))) - plan.UniqueId = flex.StringToFramework(ctx, output.Role.RoleId) + plan.UniqueID = flex.StringToFramework(ctx, output.Role.RoleId) // last steps? resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) @@ -453,6 +653,7 @@ func (r *resourceIamRole) ImportState(ctx context.Context, request resource.Impo } func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + fmt.Println("Hitting modify plan!") r.SetTagsAll(ctx, request, response) } @@ -508,7 +709,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.Description = flex.StringToFramework(ctx, role.Description) state.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(role.RoleName))) state.MaxSessionDuration = flex.Int64ToFramework(ctx, role.MaxSessionDuration) - state.UniqueId = flex.StringToFramework(ctx, role.RoleId) + state.UniqueID = flex.StringToFramework(ctx, role.RoleId) if state.ForceDetachPolicies.IsNull() { // TODO: better way to do this that is more framework friendly? From 51b21bc213378cbef6dfd582f1d8eb17ede92641 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Thu, 25 Jan 2024 23:26:51 -0500 Subject: [PATCH 082/118] ok got ignore order testing working somehow --- internal/service/iam/role.go | 31 +++++-- internal/service/iam/role_test.go | 140 +++++++++++++++--------------- 2 files changed, 96 insertions(+), 75 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 7a015ab77eb..d052fb4fd58 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -174,10 +174,10 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // ElementType: types.StringType, ElementType: fwtypes.IAMPolicyType, Optional: true, - // PlanModifiers: []planmodifier.Map{ - // EditPlanForSameReorderedPolicies(), - // // TODO: custom plan modifier for something like editing plan is fine - // }, + PlanModifiers: []planmodifier.Map{ + EditPlanForSameReorderedPolicies(), + // TODO: custom plan modifier for something like editing plan is fine + }, // TODO: custom validator for name stuff? // TODO: validators and name func for both // "name": { @@ -463,7 +463,7 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade } // TODO: fix this later? - roleDataCurrent.NamePrefix = types.StringNull() + // roleDataCurrent.NamePrefix = types.StringNull() // if roleDataV0.NamePrefix.ValueString() == "" { // roleDataCurrent.NamePrefix = types.StringNull() @@ -654,6 +654,27 @@ func (r *resourceIamRole) ImportState(ctx context.Context, request resource.Impo func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { fmt.Println("Hitting modify plan!") + if !request.Plan.Raw.IsNull() && !request.State.Raw.IsNull() { + var state, plan resourceIamRoleData + + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + + if response.Diagnostics.HasError() { + return + } + + response.Diagnostics.Append(request.Plan.Get(ctx, &plan)...) + + if response.Diagnostics.HasError() { + return + } + + if state.NamePrefix.ValueString() == plan.NamePrefix.ValueString() { + response.Diagnostics.Append(response.Plan.SetAttribute(ctx, path.Root("name_prefix"), state.NamePrefix)...) + // response.Plan.SetAttribute(ctx, path.Root("name_prefix"), state.NamePrefix) + } + + } r.SetTagsAll(ctx, request, response) } diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 50bbb90f1b3..5b8025b0036 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -53,41 +53,41 @@ func TestAccIAMRole_basic(t *testing.T) { }) } -func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" +// func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - ExternalProviders: map[string]resource.ExternalProvider{ - "aws": { - Source: "hashicorp/aws", - VersionConstraint: "5.33.0", - }, - }, - Config: testAccRoleConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "path", "/"), - resource.TestCheckResourceAttrSet(resourceName, "create_date"), - resource.TestCheckResourceAttrSet(resourceName, "unique_id"), - resource.TestCheckResourceAttrSet(resourceName, "arn"), - ), - }, - { - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Config: testAccRoleConfig_basic(rName), - PlanOnly: true, - }, - }, - }) -} +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// ExternalProviders: map[string]resource.ExternalProvider{ +// "aws": { +// Source: "hashicorp/aws", +// VersionConstraint: "5.33.0", +// }, +// }, +// Config: testAccRoleConfig_basic(rName), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// resource.TestCheckResourceAttr(resourceName, "path", "/"), +// resource.TestCheckResourceAttrSet(resourceName, "create_date"), +// resource.TestCheckResourceAttrSet(resourceName, "unique_id"), +// resource.TestCheckResourceAttrSet(resourceName, "arn"), +// ), +// }, +// { +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// Config: testAccRoleConfig_basic(rName), +// PlanOnly: true, +// }, +// }, +// }) +// } func TestAccIAMRole_description(t *testing.T) { ctx := acctest.Context(t) @@ -747,43 +747,43 @@ func TestAccIAMRole_InlinePolicy_badJSON(t *testing.T) { // TODO: So close on this, for some reason namePrefix is showing up when Plan is modified... very annoying // // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19444 -// func TestAccIAMRole_InlinePolicy_ignoreOrder(t *testing.T) { -// ctx := acctest.Context(t) -// var role iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" +func TestAccIAMRole_InlinePolicy_ignoreOrder(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRoleConfig_policyInlineActionOrder(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &role), -// resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), -// resource.TestCheckResourceAttr(resourceName, "name", rName), -// resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), -// ), -// }, -// { -// Config: testAccRoleConfig_policyInlineActionOrder(rName), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_policyInlineActionNewOrder(rName), -// PlanOnly: true, -// }, -// { -// Config: testAccRoleConfig_policyInlineActionOrderActualDiff(rName), -// PlanOnly: true, -// ExpectNonEmptyPlan: true, -// }, -// }, -// }) -// } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRoleConfig_policyInlineActionOrder(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + ), + }, + { + Config: testAccRoleConfig_policyInlineActionOrder(rName), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_policyInlineActionNewOrder(rName), + PlanOnly: true, + }, + { + Config: testAccRoleConfig_policyInlineActionOrderActualDiff(rName), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, + }, + }) +} func TestAccIAMRole_InlinePolicy_empty(t *testing.T) { ctx := acctest.Context(t) From 83202bbd470b7ea672072ed28a1d47c709cfb7b1 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 26 Jan 2024 20:12:03 -0500 Subject: [PATCH 083/118] basline upgrade test working --- internal/service/iam/role.go | 66 +++++++++++++----------- internal/service/iam/role_test.go | 84 ++++++++++++++++++------------- 2 files changed, 85 insertions(+), 65 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index d052fb4fd58..5a2bc4a31f5 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -315,20 +315,20 @@ func oldSDKIAMRoleSchema(ctx context.Context) schema.Schema { Computed: true, }, "description": schema.StringAttribute{ + Default: stringdefault.StaticString(""), + Validators: []validator.String{ + stringvalidator.LengthBetween(0, 1000), + }, Optional: true, - // TODO Validate, + Computed: true, }, "force_detach_policies": schema.BoolAttribute{ Optional: true, - // TODO Default:false, - }, - "id": // TODO framework.IDAttribute() - schema.StringAttribute{ Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, + Default: booldefault.StaticBool(false), + // TODO Default:false, }, + "id": framework.IDAttribute(), "managed_policy_arns": schema.SetAttribute{ ElementType: types.StringType, Optional: true, @@ -336,24 +336,30 @@ func oldSDKIAMRoleSchema(ctx context.Context) schema.Schema { }, "max_session_duration": schema.Int64Attribute{ Optional: true, - // TODO Default:3600, - // TODO Validate, }, "name": schema.StringAttribute{ Optional: true, Computed: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), + stringplanmodifier.RequiresReplaceIfConfigured(), + stringplanmodifier.UseStateForUnknown(), + }, + Validators: []validator.String{ + stringvalidator.LengthAtMost(roleNameMaxLen), }, - // TODO Validate, }, "name_prefix": schema.StringAttribute{ Optional: true, Computed: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), + stringplanmodifier.RequiresReplaceIfConfigured(), + }, + Validators: []validator.String{ + stringvalidator.LengthAtMost(roleNamePrefixMaxLen), + stringvalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("name"), + ), }, - // TODO Validate, }, "path": schema.StringAttribute{ Optional: true, @@ -414,7 +420,7 @@ func (r *resourceIamRole) UpgradeState(ctx context.Context) map[int64]resource.S // TODO: ok finish working on this to perform upgrade cleanly func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { - fmt.Println("Top of upgrade") + fmt.Println("Top of state upgrade") type resourceIamRoleDataV0 struct { ARN types.String `tfsdk:"arn"` AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` @@ -455,11 +461,9 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade Name: roleDataV0.Name, Path: roleDataV0.Path, UniqueID: roleDataV0.UniqueID, - // NamePrefix: roleDataV0.NamePrefix, - Tags: roleDataV0.Tags, - TagsAll: roleDataV0.TagsAll, - // ManagedPolicyArns types.Set `tfsdk:"managed_policy_arns"` - // InlinePolicy types.Set `tfsdk:"inline_policy"` + NamePrefix: roleDataV0.NamePrefix, + Tags: roleDataV0.Tags, + TagsAll: roleDataV0.TagsAll, } // TODO: fix this later? @@ -471,18 +475,21 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade // } // TODO: do something with this once I get to that test - var policyARNs []string - roleDataCurrent.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) + // var policyARNs []string + // roleDataCurrent.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) + roleDataCurrent.ManagedPolicyArns = types.SetNull(fwtypes.ARNType) // TODO: do something with this once I get to that test - temp := make(map[string]string) - roleDataCurrent.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, temp) + // temp := make(map[string]string) + // roleDataCurrent.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, temp) + roleDataCurrent.InlinePolicies = types.MapNull(fwtypes.IAMPolicyType) // TODO: update this to be string is empty check? - // if !roleDataV0.PermissionsBoundary.IsNull() { - // fmt.Println("permission boundary found") - // roleDataCurrent.PermissionsBoundary = fwtypes.ARNValue(roleDataV0.PermissionsBoundary.ValueString()) - // } + if roleDataV0.PermissionsBoundary.ValueString() != "" { + roleDataCurrent.PermissionsBoundary = fwtypes.ARNValue(roleDataV0.PermissionsBoundary.ValueString()) + } else { + roleDataCurrent.PermissionsBoundary = fwtypes.ARNNull() + } // var managedPolicies []string // resp.Diagnostics.Append(plan.ManagedPolicyArns.ElementsAs(ctx, &managedPolicies, false)...) @@ -496,7 +503,7 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade diags := resp.State.Set(ctx, roleDataCurrent) resp.Diagnostics.Append(diags...) - fmt.Println("Bottom of upgrade") + fmt.Println("Bottom of state upgrade") } func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -671,7 +678,6 @@ func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.Modif if state.NamePrefix.ValueString() == plan.NamePrefix.ValueString() { response.Diagnostics.Append(response.Plan.SetAttribute(ctx, path.Root("name_prefix"), state.NamePrefix)...) - // response.Plan.SetAttribute(ctx, path.Root("name_prefix"), state.NamePrefix) } } diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 5b8025b0036..b6b7560591a 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -53,41 +53,55 @@ func TestAccIAMRole_basic(t *testing.T) { }) } -// func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" - -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// ExternalProviders: map[string]resource.ExternalProvider{ -// "aws": { -// Source: "hashicorp/aws", -// VersionConstraint: "5.33.0", -// }, -// }, -// Config: testAccRoleConfig_basic(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// resource.TestCheckResourceAttr(resourceName, "path", "/"), -// resource.TestCheckResourceAttrSet(resourceName, "create_date"), -// resource.TestCheckResourceAttrSet(resourceName, "unique_id"), -// resource.TestCheckResourceAttrSet(resourceName, "arn"), -// ), -// }, -// { -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// Config: testAccRoleConfig_basic(rName), -// PlanOnly: true, -// }, -// }, -// }) -// } +// can't just check plan because of state diffs, rather apply new one and make sure everything +// is as expected before and after upgrade +func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.33.0", + }, + }, + Config: testAccRoleConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttrSet(resourceName, "create_date"), + resource.TestCheckResourceAttrSet(resourceName, "unique_id"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccRoleConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttrSet(resourceName, "create_date"), + resource.TestCheckResourceAttrSet(resourceName, "unique_id"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), + ), + }, + }, + }) +} func TestAccIAMRole_description(t *testing.T) { ctx := acctest.Context(t) From 55049ad99915566f34df4c4d4c5f58380ae2a2cc Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 26 Jan 2024 20:54:51 -0500 Subject: [PATCH 084/118] update provider on upgrade tests --- internal/service/iam/role_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index b6b7560591a..058f0f0f4cd 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -70,7 +70,7 @@ func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { ExternalProviders: map[string]resource.ExternalProvider{ "aws": { Source: "hashicorp/aws", - VersionConstraint: "5.33.0", + VersionConstraint: "5.34.0", }, }, Config: testAccRoleConfig_basic(rName), From b57ec16fde326ed75cf30ea805fcaeb5d019dbd7 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 26 Jan 2024 21:17:03 -0500 Subject: [PATCH 085/118] more upgrade migration checks --- internal/service/iam/role_test.go | 146 +++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 11 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 058f0f0f4cd..899d5874341 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -53,8 +53,6 @@ func TestAccIAMRole_basic(t *testing.T) { }) } -// can't just check plan because of state diffs, rather apply new one and make sure everything -// is as expected before and after upgrade func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { ctx := acctest.Context(t) var conf iam.Role @@ -81,6 +79,7 @@ func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttrSet(resourceName, "create_date"), resource.TestCheckResourceAttrSet(resourceName, "unique_id"), + resource.TestCheckResourceAttrSet(resourceName, "assume_role_policy"), resource.TestCheckResourceAttrSet(resourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), ), @@ -95,6 +94,7 @@ func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttrSet(resourceName, "create_date"), resource.TestCheckResourceAttrSet(resourceName, "unique_id"), + resource.TestCheckResourceAttrSet(resourceName, "assume_role_policy"), resource.TestCheckResourceAttrSet(resourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), ), @@ -204,6 +204,8 @@ func TestAccIAMRole_namePrefix(t *testing.T) { }) } +// TODO: name prefix test + func TestAccIAMRole_testNameChange(t *testing.T) { ctx := acctest.Context(t) var conf iam.Role @@ -548,6 +550,42 @@ func TestAccIAMRole_maxSessionDuration(t *testing.T) { }) } +func TestAccIAMRole_MigrateFromPluginSDK_MaxSessionDuration(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.34.0", + }, + }, + Config: testAccRoleConfig_maxSessionDuration(rName, 3700), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3700"), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccRoleConfig_maxSessionDuration(rName, 3700), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3700"), + ), + }, + }, + }) +} + func TestAccIAMRole_permissionsBoundary(t *testing.T) { ctx := acctest.Context(t) var role iam.Role @@ -641,6 +679,45 @@ func TestAccIAMRole_permissionsBoundary(t *testing.T) { }) } +func TestAccIAMRole_MigrateFromPluginSDK_PermissionBoundary(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + var role iam.Role + resourceName := "aws_iam_role.test" + permissionsBoundary1 := fmt.Sprintf("arn:%s:iam::aws:policy/AdministratorAccess", acctest.Partition()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.34.0", + }, + }, + Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), + testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), + testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), + ), + }, + }, + }) +} + func TestAccIAMRole_tags(t *testing.T) { ctx := acctest.Context(t) var role iam.Role @@ -700,9 +777,7 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { testAccCheckRoleExists(ctx, resourceName, &role), resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), resource.TestCheckResourceAttr(resourceName, "name", rName), - // TODO: remove this once we add managed_policy_arns - // should be null? - // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), ), }, { @@ -710,9 +785,7 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &role), resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "2"), - // TODO: remove this once we add managed_policy_arns - // should be null? - // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), ), }, { @@ -720,9 +793,7 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckRoleExists(ctx, resourceName, &role), resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), - // TODO: remove this once we add managed_policy_arns - // should be null? - // resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), ), }, { @@ -740,6 +811,57 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { }) } +// func TestAccIAMRole_MigrateFromPluginSDK_InlinePolicy(t *testing.T) { +// ctx := acctest.Context(t) +// var conf iam.Role +// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) +// resourceName := "aws_iam_role.test" + +// resource.ParallelTest(t, resource.TestCase{ +// PreCheck: func() { acctest.PreCheck(ctx, t) }, +// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), +// CheckDestroy: testAccCheckRoleDestroy(ctx), +// Steps: []resource.TestStep{ +// { +// ExternalProviders: map[string]resource.ExternalProvider{ +// "aws": { +// Source: "hashicorp/aws", +// VersionConstraint: "5.34.0", +// }, +// }, +// Config: testAccRoleConfig_policyInline(rName, policyName1), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// resource.TestCheckResourceAttr(resourceName, "path", "/"), +// resource.TestCheckResourceAttr(resourceName, "name", rName), +// resource.TestCheckResourceAttr(resourceName, "description", ""), +// resource.TestCheckResourceAttrSet(resourceName, "create_date"), +// resource.TestCheckResourceAttrSet(resourceName, "unique_id"), +// resource.TestCheckResourceAttrSet(resourceName, "arn"), +// resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), +// resource.TestCheckResourceAttr(resourceName, "inline_policies.#", "1"), +// ), +// }, +// { +// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, +// Config: testAccRoleConfig_policyInline(rName, policyName1), +// Check: resource.ComposeTestCheckFunc( +// testAccCheckRoleExists(ctx, resourceName, &conf), +// resource.TestCheckResourceAttr(resourceName, "path", "/"), +// resource.TestCheckResourceAttr(resourceName, "name", rName), +// resource.TestCheckResourceAttr(resourceName, "description", ""), +// resource.TestCheckResourceAttrSet(resourceName, "create_date"), +// resource.TestCheckResourceAttrSet(resourceName, "unique_id"), +// resource.TestCheckResourceAttrSet(resourceName, "arn"), +// resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), +// resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), +// ), +// }, +// }, +// }) +// } + func TestAccIAMRole_InlinePolicy_badJSON(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -872,6 +994,8 @@ func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { }) } +// TODO: Managed policy arns upgrade test + func TestAccIAMRole_ManagedPolicy_badARN(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) From eebb61935e3ae8a416d3aa7fca0272e34670a942 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Fri, 26 Jan 2024 23:48:14 -0500 Subject: [PATCH 086/118] stuck... --- internal/service/iam/role.go | 29 +----- internal/service/iam/role_test.go | 152 ++++++++++++++++++------------ 2 files changed, 95 insertions(+), 86 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 5a2bc4a31f5..139cccb44d3 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -71,16 +71,13 @@ func EditPlanForSameReorderedPolicies() planmodifier.Map { return editPlanForSameReorderedPolicies{} } -// editPlanForSameReorderedPolicies implements the plan modifier. type editPlanForSameReorderedPolicies struct{} -// Description returns a human-readable description of the plan modifier. // TODO: edit this once we get working func (m editPlanForSameReorderedPolicies) Description(_ context.Context) string { return "Once set, the value of this attribute in state will not change." } -// MarkdownDescription returns a markdown description of the plan modifier. // TODO: edit this once we get working func (m editPlanForSameReorderedPolicies) MarkdownDescription(_ context.Context) string { return "Once set, the value of this attribute in state will not change." @@ -102,12 +99,9 @@ func (m editPlanForSameReorderedPolicies) PlanModifyMap(ctx context.Context, req plan_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, req.PlanValue) if len(plan_inline_policies_map) == 0 { - // fmt.Println("empty plan map exiting") return } state_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, req.StateValue) - // fmt.Println(fmt.Sprintf("state_inline_policies_map: %+v", state_inline_policies_map)) - // fmt.Println(fmt.Sprintf("plan_inline_policies_map: %+v", plan_inline_policies_map)) // If policies match, set plan for policy to use state version so that we don't see if diff bc ordering does not matter for name, plan_policy_doc := range plan_inline_policies_map { @@ -171,7 +165,6 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, // TODO: maybe mapof of IAMPolicytype? "inline_policies": schema.MapAttribute{ - // ElementType: types.StringType, ElementType: fwtypes.IAMPolicyType, Optional: true, PlanModifiers: []planmodifier.Map{ @@ -507,7 +500,6 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade } func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - fmt.Println("Hitting top of Create") conn := r.Meta().IAMConn(ctx) var plan resourceIamRoleData @@ -615,9 +607,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(aws.StringValue(output.Role.RoleName))) plan.UniqueID = flex.StringToFramework(ctx, output.Role.RoleId) - // last steps? resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - fmt.Println("Bottom of Create") } func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { @@ -655,12 +645,10 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, } func (r *resourceIamRole) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { - fmt.Println("Top of Import") resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response) } func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { - fmt.Println("Hitting modify plan!") if !request.Plan.Raw.IsNull() && !request.State.Raw.IsNull() { var state, plan resourceIamRoleData @@ -676,6 +664,10 @@ func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.Modif return } + if state.Description.ValueString() == plan.Description.ValueString() { + response.Diagnostics.Append(response.Plan.SetAttribute(ctx, path.Root("description"), state.Description)...) + } + if state.NamePrefix.ValueString() == plan.NamePrefix.ValueString() { response.Diagnostics.Append(response.Plan.SetAttribute(ctx, path.Root("name_prefix"), state.NamePrefix)...) } @@ -685,7 +677,6 @@ func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.Modif } func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - fmt.Println("Top of Read") conn := r.Meta().IAMConn(ctx) var state resourceIamRoleData @@ -702,9 +693,6 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res // NOTE: Same issue here, I left old conditional here as example, not sure what else can/should be done // if !d.IsNewResource() && tfresource.NotFound(err) { if tfresource.NotFound(err) { - // log.Printf("[WARN] IAM Role (%s) not found, removing from state", d.Id()) - // d.SetId("") - // return diags resp.State.RemoveResource(ctx) return } @@ -784,10 +772,8 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res var configPoliciesList []*iam.PutRolePolicyInput inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), inline_policies_map) - fmt.Println(fmt.Sprintf("configPoliciesList: %+v", configPoliciesList)) if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { - fmt.Println("found different inline policies!") state.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, flattenRoleInlinePolicies(inlinePolicies)) } } @@ -808,11 +794,9 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res setTagsOut(ctx, role.Tags) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) - fmt.Println("Bottom of Read") } func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - fmt.Println("Top of Update") conn := r.Meta().IAMConn(ctx) var plan, state resourceIamRoleData @@ -932,8 +916,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } if !plan.InlinePolicies.Equal(state.InlinePolicies) && inlinePoliciesActualDiff(ctx, &plan, &state) { - fmt.Println("Found inline policies changes!") - old_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) new_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) @@ -1066,7 +1048,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, plan.NamePrefix = flex.StringToFramework(ctx, create.NamePrefixFromName(plan.Name.ValueString())) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - fmt.Println("Hit bottom of update") } func FindRoleByName(ctx context.Context, conn *iam.IAM, name string) (*iam.Role, error) { @@ -1340,8 +1321,6 @@ func expandRoleInlinePolicies(roleName string, tfPoliciesMap map[string]string) var apiObjects []*iam.PutRolePolicyInput for policyName, policyDocument := range tfPoliciesMap { - fmt.Println(fmt.Sprintf("policyName: %s", policyName)) - // fmt.Println(fmt.Sprintf("policyDocument: %s", policyDocument)) apiObject := expandRoleInlinePolicy(roleName, policyName, policyDocument) if apiObject == nil { diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 899d5874341..e1231dd0124 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -87,17 +87,7 @@ func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Config: testAccRoleConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "path", "/"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "description", ""), - resource.TestCheckResourceAttrSet(resourceName, "create_date"), - resource.TestCheckResourceAttrSet(resourceName, "unique_id"), - resource.TestCheckResourceAttrSet(resourceName, "assume_role_policy"), - resource.TestCheckResourceAttrSet(resourceName, "arn"), - resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), - ), + PlanOnly: true, }, }, }) @@ -811,56 +801,56 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { }) } -// func TestAccIAMRole_MigrateFromPluginSDK_InlinePolicy(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.Role -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role.test" - -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// CheckDestroy: testAccCheckRoleDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// ExternalProviders: map[string]resource.ExternalProvider{ -// "aws": { -// Source: "hashicorp/aws", -// VersionConstraint: "5.34.0", -// }, -// }, -// Config: testAccRoleConfig_policyInline(rName, policyName1), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// resource.TestCheckResourceAttr(resourceName, "path", "/"), -// resource.TestCheckResourceAttr(resourceName, "name", rName), -// resource.TestCheckResourceAttr(resourceName, "description", ""), -// resource.TestCheckResourceAttrSet(resourceName, "create_date"), -// resource.TestCheckResourceAttrSet(resourceName, "unique_id"), -// resource.TestCheckResourceAttrSet(resourceName, "arn"), -// resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), -// resource.TestCheckResourceAttr(resourceName, "inline_policies.#", "1"), -// ), -// }, -// { -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// Config: testAccRoleConfig_policyInline(rName, policyName1), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRoleExists(ctx, resourceName, &conf), -// resource.TestCheckResourceAttr(resourceName, "path", "/"), -// resource.TestCheckResourceAttr(resourceName, "name", rName), -// resource.TestCheckResourceAttr(resourceName, "description", ""), -// resource.TestCheckResourceAttrSet(resourceName, "create_date"), -// resource.TestCheckResourceAttrSet(resourceName, "unique_id"), -// resource.TestCheckResourceAttrSet(resourceName, "arn"), -// resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), -// resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), -// ), -// }, -// }, -// }) -// } +func TestAccIAMRole_MigrateFromPluginSDK_InlinePolicy(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.34.0", + }, + }, + Config: testAccRoleConfig_policyInlineLegacy(rName, policyName1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttrSet(resourceName, "create_date"), + resource.TestCheckResourceAttrSet(resourceName, "unique_id"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), + resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccRoleConfig_policyInline(rName, policyName1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttrSet(resourceName, "create_date"), + resource.TestCheckResourceAttrSet(resourceName, "unique_id"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), + resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), + ), + }, + }, + }) +} func TestAccIAMRole_InlinePolicy_badJSON(t *testing.T) { ctx := acctest.Context(t) @@ -2042,6 +2032,46 @@ EOF `, roleName) } +func testAccRoleConfig_policyInlineLegacy(roleName, policyName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Action = "sts:AssumeRole", + Principal = { + Service = "ec2.${data.aws_partition.current.dns_suffix}", + } + Effect = "Allow" + Sid = "" + }] + }) + + inline_policy { + name = %[2]q + policy = < Date: Sat, 27 Jan 2024 11:10:20 -0500 Subject: [PATCH 087/118] fix test terraform fmt --- internal/service/iam/role_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index e1231dd0124..16fc7ee9594 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -2116,7 +2116,7 @@ func testAccRoleConfig_policyInlineUpdate(roleName, policyName2, policyName3 str data "aws_partition" "current" {} locals { - inline_policy_doc = < Date: Sat, 27 Jan 2024 11:21:18 -0500 Subject: [PATCH 088/118] remove arn validators --- internal/framework/validators/arn.go | 101 -------------- internal/framework/validators/arn_test.go | 158 ---------------------- 2 files changed, 259 deletions(-) delete mode 100644 internal/framework/validators/arn.go delete mode 100644 internal/framework/validators/arn_test.go diff --git a/internal/framework/validators/arn.go b/internal/framework/validators/arn.go deleted file mode 100644 index e9cdd3ae20f..00000000000 --- a/internal/framework/validators/arn.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package validators - -import ( - "context" - "fmt" - - "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" -) - -var accountIDRegexp = regexache.MustCompile(`^(aws|aws-managed|third-party|\d{12}|cw.{10})$`) -var partitionRegexp = regexache.MustCompile(`^aws(-[a-z]+)*$`) -var regionRegexp = regexache.MustCompile(`^[a-z]{2}(-[a-z]+)+-\d$`) - -type arnValidator struct{} - -// Description describes the validation in plain text formatting. -func (validator arnValidator) Description(_ context.Context) string { - return "value must be a valid arn" -} - -// MarkdownDescription describes the validation in Markdown formatting. -func (validator arnValidator) MarkdownDescription(ctx context.Context) string { - return validator.Description(ctx) -} - -func (validator arnValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { - if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { - return - } - - value := request.ConfigValue.ValueString() - parsedARN, err := arn.Parse(value) - - if err != nil { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - validator.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: %s", value, err), - )) - return - } - - if parsedARN.Partition == "" { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - validator.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: missing partition value", value), - )) - return - } else if !partitionRegexp.MatchString(parsedARN.Partition) { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - validator.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: invalid partition value (expecting to match regular expression: %s)", value, partitionRegexp), - )) - return - } - - if parsedARN.Region != "" && !regionRegexp.MatchString(parsedARN.Region) { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - validator.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: invalid region value (expecting to match regular expression: %s)", value, regionRegexp), - )) - return - } - - if parsedARN.AccountID != "" && !accountIDRegexp.MatchString(parsedARN.AccountID) { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - validator.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: invalid account ID value (expecting to match regular expression: %s)", value, accountIDRegexp), - )) - return - } - - if parsedARN.Resource == "" { - response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( - request.Path, - validator.Description(ctx), - fmt.Sprintf("(%s) is an invalid ARN: missing resource value", value), - )) - return - } -} - -// ARN returns a string validator which ensures that any configured -// attribute value: -// -// - Is a string, which represents a valid ARN. -// -// Null (unconfigured) and unknown (known after apply) values are skipped. -func ARN() validator.String { - return arnValidator{} -} diff --git a/internal/framework/validators/arn_test.go b/internal/framework/validators/arn_test.go deleted file mode 100644 index bb5d6229f54..00000000000 --- a/internal/framework/validators/arn_test.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package validators_test - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" - fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators" -) - -func TestARNValidator(t *testing.T) { - t.Parallel() - - type testCase struct { - val types.String - expectedDiagnostics diag.Diagnostics - } - tests := map[string]testCase{ - "unknown String": { - val: types.StringUnknown(), - }, - "null String": { - val: types.StringNull(), - }, - "Beanstalk": { - val: types.StringValue("arn:aws:elasticbeanstalk:us-east-1:123456789012:environment/My App/MyEnvironment"), - }, - "IAM User": { - val: types.StringValue("arn:aws:iam::123456789012:user/David"), - }, - "Managed IAM policy": { - val: types.StringValue("arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess"), - }, - "ImageBuilder": { - val: types.StringValue("arn:aws:imagebuilder:us-east-1:third-party:component/my-component"), - }, - "RDS": { - val: types.StringValue("arn:aws:rds:eu-west-1:123456789012:db:mysql-db"), - }, - "S3 object": { - val: types.StringValue("arn:aws:s3:::my_corporate_bucket/exampleobject.png"), - }, - "CloudWatch Rule": { - val: types.StringValue("arn:aws:events:us-east-1:319201112229:rule/rule_name"), - }, - "Lambda function": { - val: types.StringValue("arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction"), - }, - "Lambda func qualifier": { - val: types.StringValue("arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction:Qualifier"), - }, - "China EC2 ARN": { - val: types.StringValue("arn:aws-cn:ec2:cn-north-1:123456789012:instance/i-12345678"), - }, - "China S3 ARN": { - val: types.StringValue("arn:aws-cn:s3:::bucket/object"), - }, - "C2S EC2 ARN": { - val: types.StringValue("arn:aws-iso:ec2:us-iso-east-1:123456789012:instance/i-12345678"), - }, - "C2S S3 ARN": { - val: types.StringValue("arn:aws-iso:s3:::bucket/object"), - }, - "SC2S EC2 ARN": { - val: types.StringValue("arn:aws-iso-b:ec2:us-isob-east-1:123456789012:instance/i-12345678"), - }, - "SC2S S3 ARN": { - val: types.StringValue("arn:aws-iso-b:s3:::bucket/object"), - }, - "GovCloud EC2 ARN": { - val: types.StringValue("arn:aws-us-gov:ec2:us-gov-west-1:123456789012:instance/i-12345678"), - }, - "GovCloud S3 ARN": { - val: types.StringValue("arn:aws-us-gov:s3:::bucket/object"), - }, - "Cloudwatch Alarm": { - val: types.StringValue("arn:aws:cloudwatch::cw0000000000:alarm:my-alarm"), - }, - "Invalid prefix 1": { - val: types.StringValue("arn"), - expectedDiagnostics: diag.Diagnostics{ - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "Invalid Attribute Value", - `Attribute test value must be a valid arn, got: (arn) is an invalid ARN: arn: invalid prefix`, - ), - }, - }, - "Invalid prefix 2": { - val: types.StringValue("123456789012"), - expectedDiagnostics: diag.Diagnostics{ - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "Invalid Attribute Value", - `Attribute test value must be a valid arn, got: (123456789012) is an invalid ARN: arn: invalid prefix`, - ), - }, - }, - "Not enough sections 1": { - val: types.StringValue("arn:aws"), - expectedDiagnostics: diag.Diagnostics{ - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "Invalid Attribute Value", - `Attribute test value must be a valid arn, got: (arn:aws) is an invalid ARN: arn: not enough sections`, - ), - }, - }, - "Not enough sections 2": { - val: types.StringValue("arn:aws:logs"), - expectedDiagnostics: diag.Diagnostics{ - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "Invalid Attribute Value", - `Attribute test value must be a valid arn, got: (arn:aws:logs) is an invalid ARN: arn: not enough sections`, - ), - }, - }, - "Invalid region value": { - val: types.StringValue("arn:aws:logs:region:*:*"), - expectedDiagnostics: diag.Diagnostics{ - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "Invalid Attribute Value", - `Attribute test value must be a valid arn, got: (arn:aws:logs:region:*:*) is an invalid ARN: invalid region value (expecting to match regular expression: ^[a-z]{2}(-[a-z]+)+-\d$)`, - ), - }, - }, - } - - for name, test := range tests { - name, test := name, test - t.Run(name, func(t *testing.T) { - t.Parallel() - - ctx := context.Background() - - request := validator.StringRequest{ - Path: path.Root("test"), - PathExpression: path.MatchRoot("test"), - ConfigValue: test.val, - } - response := validator.StringResponse{} - fwvalidators.ARN().ValidateString(ctx, request, &response) - - if diff := cmp.Diff(response.Diagnostics, test.expectedDiagnostics); diff != "" { - t.Errorf("unexpected diagnostics difference: %s", diff) - } - }) - } -} From f303888bc8df4caa09a6025967efc0cd770ec0fc Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 27 Jan 2024 12:39:54 -0500 Subject: [PATCH 089/118] fix semgrep error --- internal/service/iam/role.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 139cccb44d3..3cf0efcbad2 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -47,7 +47,7 @@ import ( const ( roleNameMaxLen = 64 roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength - ResNameIamRole = "IAM Role" + ResNameIAMRole = "IAM Role" ) // @FrameworkResource(name="Role") @@ -511,7 +511,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, plan.AssumeRolePolicy.String(), nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIAMRole, plan.AssumeRolePolicy.String(), nil), errors.New(fmt.Sprintf("assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err)).Error(), ) return @@ -546,7 +546,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIAMRole, name, nil), err.Error(), ) return @@ -559,7 +559,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, policies := expandRoleInlinePolicies(roleName, inline_policies_map) if err := r.addRoleInlinePolicies(ctx, policies); err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIAMRole, name, nil), err.Error(), ) return @@ -574,7 +574,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } if err := r.addRoleManagedPolicies(ctx, roleName, managedPolicies); err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIamRole, name, nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIAMRole, name, nil), err.Error(), ) return @@ -593,7 +593,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, fmt.Sprintf("%s tags", ResNameIamRole), name, nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, fmt.Sprintf("%s tags", ResNameIAMRole), name, nil), err.Error(), ) return From 31c4e177745986712e3e887e3889371090752ff7 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 27 Jan 2024 12:41:08 -0500 Subject: [PATCH 090/118] another fix --- internal/service/iam/role.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 3cf0efcbad2..9617931be38 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -47,7 +47,7 @@ import ( const ( roleNameMaxLen = 64 roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength - ResNameIAMRole = "IAM Role" + ResNameRole = "IAM Role" ) // @FrameworkResource(name="Role") @@ -511,7 +511,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIAMRole, plan.AssumeRolePolicy.String(), nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameRole, plan.AssumeRolePolicy.String(), nil), errors.New(fmt.Sprintf("assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err)).Error(), ) return @@ -546,7 +546,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIAMRole, name, nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameRole, name, nil), err.Error(), ) return @@ -559,7 +559,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, policies := expandRoleInlinePolicies(roleName, inline_policies_map) if err := r.addRoleInlinePolicies(ctx, policies); err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIAMRole, name, nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameRole, name, nil), err.Error(), ) return @@ -574,7 +574,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, } if err := r.addRoleManagedPolicies(ctx, roleName, managedPolicies); err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameIAMRole, name, nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameRole, name, nil), err.Error(), ) return @@ -593,7 +593,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, fmt.Sprintf("%s tags", ResNameIAMRole), name, nil), + create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, fmt.Sprintf("%s tags", ResNameRole), name, nil), err.Error(), ) return From 22502fd4d6df54a0e807d3e4b0da0eadff8cafea Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 27 Jan 2024 12:41:40 -0500 Subject: [PATCH 091/118] another fix --- internal/service/iam/role.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 9617931be38..5ba943e9f73 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -293,7 +293,7 @@ type resourceIamRoleData struct { TagsAll types.Map `tfsdk:"tags_all"` } -func oldSDKIAMRoleSchema(ctx context.Context) schema.Schema { +func oldSDKRoleSchema(ctx context.Context) schema.Schema { return schema.Schema{ Version: 0, Attributes: map[string]schema.Attribute{ @@ -401,7 +401,7 @@ func oldSDKIAMRoleSchema(ctx context.Context) schema.Schema { } func (r *resourceIamRole) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { - schemaV0 := oldSDKIAMRoleSchema(ctx) + schemaV0 := oldSDKRoleSchema(ctx) return map[int64]resource.StateUpgrader{ 0: { From 0aebb5e5ddaae9d9fe0dde96db380d9a8ebb2e3f Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 3 Feb 2024 18:19:46 -0500 Subject: [PATCH 092/118] testing --- internal/service/iam/role.go | 46 +++++++------------------------ internal/service/iam/role_test.go | 2 +- 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 5ba943e9f73..0aef27c8144 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -47,7 +47,7 @@ import ( const ( roleNameMaxLen = 64 roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength - ResNameRole = "IAM Role" + ResNameRole = "IAM Role" ) // @FrameworkResource(name="Role") @@ -67,6 +67,7 @@ func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataR response.TypeName = "aws_iam_role" } +// TODO: move this to Modify plan if both aren't empty func EditPlanForSameReorderedPolicies() planmodifier.Map { return editPlanForSameReorderedPolicies{} } @@ -415,7 +416,7 @@ func (r *resourceIamRole) UpgradeState(ctx context.Context) map[int64]resource.S func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { fmt.Println("Top of state upgrade") type resourceIamRoleDataV0 struct { - ARN types.String `tfsdk:"arn"` + ARN types.String `tfsdk:"arn"` AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` CreateDate types.String `tfsdk:"create_date"` Description types.String `tfsdk:"description"` @@ -437,7 +438,6 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade resp.Diagnostics.Append(req.State.Get(ctx, &roleDataV0)...) if resp.Diagnostics.HasError() { - fmt.Println("There was an error :(") return } @@ -452,9 +452,13 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade ID: roleDataV0.ID, MaxSessionDuration: roleDataV0.MaxSessionDuration, Name: roleDataV0.Name, + NamePrefix: types.StringNull(), Path: roleDataV0.Path, UniqueID: roleDataV0.UniqueID, - NamePrefix: roleDataV0.NamePrefix, + // NamePrefix: roleDataV0.NamePrefix, + ManagedPolicyArns: types.SetNull(fwtypes.ARNType), + InlinePolicies: types.MapNull(fwtypes.IAMPolicyType), + PermissionsBoundary: fwtypes.ARNNull(), Tags: roleDataV0.Tags, TagsAll: roleDataV0.TagsAll, } @@ -462,43 +466,13 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade // TODO: fix this later? // roleDataCurrent.NamePrefix = types.StringNull() - // if roleDataV0.NamePrefix.ValueString() == "" { - // roleDataCurrent.NamePrefix = types.StringNull() - // // fmt.Println("Name prefix is empty!") - // } - - // TODO: do something with this once I get to that test - // var policyARNs []string - // roleDataCurrent.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) - roleDataCurrent.ManagedPolicyArns = types.SetNull(fwtypes.ARNType) - - // TODO: do something with this once I get to that test - // temp := make(map[string]string) - // roleDataCurrent.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, temp) - roleDataCurrent.InlinePolicies = types.MapNull(fwtypes.IAMPolicyType) - - // TODO: update this to be string is empty check? - if roleDataV0.PermissionsBoundary.ValueString() != "" { - roleDataCurrent.PermissionsBoundary = fwtypes.ARNValue(roleDataV0.PermissionsBoundary.ValueString()) - } else { - roleDataCurrent.PermissionsBoundary = fwtypes.ARNNull() - } - - // var managedPolicies []string - // resp.Diagnostics.Append(plan.ManagedPolicyArns.ElementsAs(ctx, &managedPolicies, false)...) - // if resp.Diagnostics.HasError() { - // return - // } - - // if jobQueueDataV0.SchedulingPolicyARN.ValueString() == "" { - // jobQueueDataV2.SchedulingPolicyARN = fwtypes.ARNNull() - // } - diags := resp.State.Set(ctx, roleDataCurrent) resp.Diagnostics.Append(diags...) fmt.Println("Bottom of state upgrade") } +// TODO: maybe refreshFromOutput + func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { conn := r.Meta().IAMConn(ctx) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 16fc7ee9594..42f83d5344d 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -68,7 +68,7 @@ func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { ExternalProviders: map[string]resource.ExternalProvider{ "aws": { Source: "hashicorp/aws", - VersionConstraint: "5.34.0", + VersionConstraint: "5.35.0", }, }, Config: testAccRoleConfig_basic(rName), From 5ad64aae2ef04e7bc6ba9b1975f1832097fee022 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 3 Feb 2024 20:20:38 -0500 Subject: [PATCH 093/118] found workaround --- internal/service/iam/role.go | 2 +- internal/service/iam/role_test.go | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 0aef27c8144..bed3239a160 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -416,7 +416,7 @@ func (r *resourceIamRole) UpgradeState(ctx context.Context) map[int64]resource.S func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { fmt.Println("Top of state upgrade") type resourceIamRoleDataV0 struct { - ARN types.String `tfsdk:"arn"` + ARN types.String `tfsdk:"arn"` AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` CreateDate types.String `tfsdk:"create_date"` Description types.String `tfsdk:"description"` diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 42f83d5344d..e27a8059e4d 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -86,8 +87,12 @@ func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { }, { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Config: testAccRoleConfig_basic(rName), - PlanOnly: true, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + Config: testAccRoleConfig_basic(rName), }, }, }) From 299c49d2a25f2bfed30addcdd1b3514eb51c872c Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 3 Feb 2024 20:33:43 -0500 Subject: [PATCH 094/118] more tests --- internal/service/iam/role.go | 16 +++++++++------- internal/service/iam/role_test.go | 32 ++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index bed3239a160..7d7eebe6c94 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -456,15 +456,17 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade Path: roleDataV0.Path, UniqueID: roleDataV0.UniqueID, // NamePrefix: roleDataV0.NamePrefix, - ManagedPolicyArns: types.SetNull(fwtypes.ARNType), - InlinePolicies: types.MapNull(fwtypes.IAMPolicyType), - PermissionsBoundary: fwtypes.ARNNull(), - Tags: roleDataV0.Tags, - TagsAll: roleDataV0.TagsAll, + ManagedPolicyArns: types.SetNull(fwtypes.ARNType), + InlinePolicies: types.MapNull(fwtypes.IAMPolicyType), + Tags: roleDataV0.Tags, + TagsAll: roleDataV0.TagsAll, } - // TODO: fix this later? - // roleDataCurrent.NamePrefix = types.StringNull() + if roleDataV0.PermissionsBoundary.ValueString() == "" { + roleDataCurrent.PermissionsBoundary = fwtypes.ARNNull() + } else { + roleDataCurrent.PermissionsBoundary = fwtypes.ARNValue(roleDataV0.PermissionsBoundary.ValueString()) + } diags := resp.State.Set(ctx, roleDataCurrent) resp.Diagnostics.Append(diags...) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index e27a8059e4d..37710f271ea 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -87,6 +87,9 @@ func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { }, { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + // NOTE: Have to update docs to reflect this because giant issue + // Can't not use PlanOnly anymore with terraform-plugin-testing + // https://github.com/hashicorp/terraform-plugin-testing/issues/256 ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectEmptyPlan(), @@ -571,11 +574,15 @@ func TestAccIAMRole_MigrateFromPluginSDK_MaxSessionDuration(t *testing.T) { }, { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Config: testAccRoleConfig_maxSessionDuration(rName, 3700), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3700"), - ), + // NOTE: Have to update docs to reflect this because giant issue + // Can't not use PlanOnly anymore with terraform-plugin-testing + // https://github.com/hashicorp/terraform-plugin-testing/issues/256 + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + Config: testAccRoleConfig_maxSessionDuration(rName, 3700), }, }, }) @@ -702,12 +709,15 @@ func TestAccIAMRole_MigrateFromPluginSDK_PermissionBoundary(t *testing.T) { }, { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), - testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), - ), + // NOTE: Have to update docs to reflect this because giant issue + // Can't not use PlanOnly anymore with terraform-plugin-testing + // https://github.com/hashicorp/terraform-plugin-testing/issues/256 + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), }, }, }) From ea011d47c4aef5c30ddd1d5223405aab8d256ae5 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 3 Feb 2024 20:38:58 -0500 Subject: [PATCH 095/118] fix inlinetest --- internal/service/iam/role_test.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 37710f271ea..de9f4166c06 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -850,18 +850,15 @@ func TestAccIAMRole_MigrateFromPluginSDK_InlinePolicy(t *testing.T) { }, { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - Config: testAccRoleConfig_policyInline(rName, policyName1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "path", "/"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "description", ""), - resource.TestCheckResourceAttrSet(resourceName, "create_date"), - resource.TestCheckResourceAttrSet(resourceName, "unique_id"), - resource.TestCheckResourceAttrSet(resourceName, "arn"), - resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), - resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), - ), + // NOTE: Have to update docs to reflect this because giant issue + // Can't not use PlanOnly anymore with terraform-plugin-testing + // https://github.com/hashicorp/terraform-plugin-testing/issues/256 + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + Config: testAccRoleConfig_policyInline(rName, policyName1), }, }, }) From a3625818ac672e85405d92dbe8a8aad0135ffac5 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sat, 3 Feb 2024 23:26:49 -0500 Subject: [PATCH 096/118] more tests --- internal/service/iam/role.go | 34 +++++++--- internal/service/iam/role_test.go | 106 +++++++++++++++++++++++++++--- 2 files changed, 122 insertions(+), 18 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 7d7eebe6c94..1c7d984d88b 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -452,14 +452,13 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade ID: roleDataV0.ID, MaxSessionDuration: roleDataV0.MaxSessionDuration, Name: roleDataV0.Name, - NamePrefix: types.StringNull(), + NamePrefix: roleDataV0.NamePrefix, Path: roleDataV0.Path, UniqueID: roleDataV0.UniqueID, - // NamePrefix: roleDataV0.NamePrefix, - ManagedPolicyArns: types.SetNull(fwtypes.ARNType), - InlinePolicies: types.MapNull(fwtypes.IAMPolicyType), - Tags: roleDataV0.Tags, - TagsAll: roleDataV0.TagsAll, + ManagedPolicyArns: types.SetNull(fwtypes.ARNType), + InlinePolicies: types.MapNull(fwtypes.IAMPolicyType), + Tags: roleDataV0.Tags, + TagsAll: roleDataV0.TagsAll, } if roleDataV0.PermissionsBoundary.ValueString() == "" { @@ -468,6 +467,24 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade roleDataCurrent.PermissionsBoundary = fwtypes.ARNValue(roleDataV0.PermissionsBoundary.ValueString()) } + type inlinePolicyData struct { + Name types.String `tfsdk:"name"` + Policy types.String `tfsdk:"policy"` + } + + var inlinePolicies []inlinePolicyData + resp.Diagnostics.Append(roleDataV0.InlinePolicy.ElementsAs(ctx, &inlinePolicies, false)...) + if resp.Diagnostics.HasError() { + fmt.Println("Hitting error of inlinePolicyData") + return + } + + inlinePoliciesMap := make(map[string]string) + for _, inlinePolicy := range inlinePolicies { + inlinePoliciesMap[inlinePolicy.Name.ValueString()] = inlinePolicy.Policy.ValueString() + } + roleDataCurrent.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, inlinePoliciesMap) + diags := resp.State.Set(ctx, roleDataCurrent) resp.Diagnostics.Append(diags...) fmt.Println("Bottom of state upgrade") @@ -531,8 +548,9 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, roleName := aws.StringValue(output.Role.RoleName) if !plan.InlinePolicies.IsNull() && !plan.InlinePolicies.IsUnknown() { - inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) - policies := expandRoleInlinePolicies(roleName, inline_policies_map) + inlinePoliciesMap := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) + + policies := expandRoleInlinePolicies(roleName, inlinePoliciesMap) if err := r.addRoleInlinePolicies(ctx, policies); err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameRole, name, nil), diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index de9f4166c06..4587d214941 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -146,6 +146,47 @@ func TestAccIAMRole_description(t *testing.T) { }) } +func TestAccIAMRole_MigrateFromPluginSDK_description(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.35.0", + }, + }, + Config: testAccRoleConfig_description(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttr(resourceName, "description", "This 1s a D3scr!pti0n with weird content: &@90ë\"'{«¡Çø}"), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + // NOTE: Have to update docs to reflect this because giant issue + // Can't not use PlanOnly anymore with terraform-plugin-testing + // https://github.com/hashicorp/terraform-plugin-testing/issues/256 + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + Config: testAccRoleConfig_description(rName), + }, + }, + }) +} + func TestAccIAMRole_nameGenerated(t *testing.T) { ctx := acctest.Context(t) var conf iam.Role @@ -202,7 +243,51 @@ func TestAccIAMRole_namePrefix(t *testing.T) { }) } -// TODO: name prefix test +// TODO: name prefix migration test +func TestAccIAMRole_MigrateFromPluginSDK_namePrefix(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Role + resourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.35.0", + }, + }, + Config: testAccRoleConfig_namePrefix(acctest.ResourcePrefix), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", acctest.ResourcePrefix), + resource.TestCheckResourceAttr(resourceName, "name_prefix", acctest.ResourcePrefix), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + // NOTE: Have to update docs to reflect this because giant issue + // Can't not use PlanOnly anymore with terraform-plugin-testing + // https://github.com/hashicorp/terraform-plugin-testing/issues/256 + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + Config: testAccRoleConfig_namePrefix(acctest.ResourcePrefix), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &conf), + acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", acctest.ResourcePrefix), + resource.TestCheckResourceAttr(resourceName, "name_prefix", acctest.ResourcePrefix), + ), + }, + }, + }) +} func TestAccIAMRole_testNameChange(t *testing.T) { ctx := acctest.Context(t) @@ -818,7 +903,7 @@ func TestAccIAMRole_InlinePolicy_basic(t *testing.T) { func TestAccIAMRole_MigrateFromPluginSDK_InlinePolicy(t *testing.T) { ctx := acctest.Context(t) - var conf iam.Role + var role iam.Role rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_iam_role.test" @@ -837,15 +922,10 @@ func TestAccIAMRole_MigrateFromPluginSDK_InlinePolicy(t *testing.T) { }, Config: testAccRoleConfig_policyInlineLegacy(rName, policyName1), Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "path", "/"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "description", ""), - resource.TestCheckResourceAttrSet(resourceName, "create_date"), - resource.TestCheckResourceAttrSet(resourceName, "unique_id"), - resource.TestCheckResourceAttrSet(resourceName, "arn"), - resource.TestCheckResourceAttr(resourceName, "force_detach_policies", "false"), + testAccCheckRoleExists(ctx, resourceName, &role), resource.TestCheckResourceAttr(resourceName, "inline_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), ), }, { @@ -859,6 +939,12 @@ func TestAccIAMRole_MigrateFromPluginSDK_InlinePolicy(t *testing.T) { }, }, Config: testAccRoleConfig_policyInline(rName, policyName1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), + ), }, }, }) From 1b8321ce3e56e0bcfbe47ef72d7d0af9679f5bba Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 01:48:33 -0500 Subject: [PATCH 097/118] add final tests --- internal/service/iam/role.go | 13 ++++---- internal/service/iam/role_test.go | 50 +++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 1c7d984d88b..e0454599ac3 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -414,7 +414,6 @@ func (r *resourceIamRole) UpgradeState(ctx context.Context) map[int64]resource.S // TODO: ok finish working on this to perform upgrade cleanly func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { - fmt.Println("Top of state upgrade") type resourceIamRoleDataV0 struct { ARN types.String `tfsdk:"arn"` AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` @@ -441,8 +440,6 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade return } - fmt.Println("Made it here!") - roleDataCurrent := resourceIamRoleData{ ARN: fwtypes.ARNValue(roleDataV0.ARN.ValueString()), AssumeRolePolicy: fwtypes.IAMPolicyValue(roleDataV0.AssumeRolePolicy.ValueString()), @@ -456,7 +453,6 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade Path: roleDataV0.Path, UniqueID: roleDataV0.UniqueID, ManagedPolicyArns: types.SetNull(fwtypes.ARNType), - InlinePolicies: types.MapNull(fwtypes.IAMPolicyType), Tags: roleDataV0.Tags, TagsAll: roleDataV0.TagsAll, } @@ -475,7 +471,6 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade var inlinePolicies []inlinePolicyData resp.Diagnostics.Append(roleDataV0.InlinePolicy.ElementsAs(ctx, &inlinePolicies, false)...) if resp.Diagnostics.HasError() { - fmt.Println("Hitting error of inlinePolicyData") return } @@ -484,10 +479,16 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade inlinePoliciesMap[inlinePolicy.Name.ValueString()] = inlinePolicy.Policy.ValueString() } roleDataCurrent.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, inlinePoliciesMap) + var policyARNs []string + + resp.Diagnostics.Append(roleDataV0.ManagedPolicyArns.ElementsAs(ctx, &policyARNs, false)...) + if resp.Diagnostics.HasError() { + return + } + roleDataCurrent.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) diags := resp.State.Set(ctx, roleDataCurrent) resp.Diagnostics.Append(diags...) - fmt.Println("Bottom of state upgrade") } // TODO: maybe refreshFromOutput diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 4587d214941..645b6c4ac61 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -939,12 +939,6 @@ func TestAccIAMRole_MigrateFromPluginSDK_InlinePolicy(t *testing.T) { }, }, Config: testAccRoleConfig_policyInline(rName, policyName1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "inline_policies.%", "1"), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "0"), - ), }, }, }) @@ -968,8 +962,6 @@ func TestAccIAMRole_InlinePolicy_badJSON(t *testing.T) { }) } -// TODO: So close on this, for some reason namePrefix is showing up when Plan is modified... very annoying - // // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19444 func TestAccIAMRole_InlinePolicy_ignoreOrder(t *testing.T) { ctx := acctest.Context(t) @@ -1082,7 +1074,47 @@ func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { }) } -// TODO: Managed policy arns upgrade test +func TestAccIAMRole_MigrateFromPluginSDK_ManagedPolicy(t *testing.T) { + ctx := acctest.Context(t) + var role iam.Role + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + CheckDestroy: testAccCheckRoleDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.34.0", + }, + }, + Config: testAccRoleConfig_policyManaged(rName, policyName1), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoleExists(ctx, resourceName, &role), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + // NOTE: Have to update docs to reflect this because giant issue + // Can't not use PlanOnly anymore with terraform-plugin-testing + // https://github.com/hashicorp/terraform-plugin-testing/issues/256 + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + Config: testAccRoleConfig_policyManaged(rName, policyName1), + }, + }, + }) +} func TestAccIAMRole_ManagedPolicy_badARN(t *testing.T) { ctx := acctest.Context(t) From c715345a82ba61442fdcb28058e6f09c7fcd3ba8 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:48:42 -0500 Subject: [PATCH 098/118] remove some tests because of other issues I was seeing with multiple upgrade tests as once --- internal/service/iam/role_test.go | 211 ------------------------------ 1 file changed, 211 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 645b6c4ac61..c5fd126ce07 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -146,47 +146,6 @@ func TestAccIAMRole_description(t *testing.T) { }) } -func TestAccIAMRole_MigrateFromPluginSDK_description(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - ExternalProviders: map[string]resource.ExternalProvider{ - "aws": { - Source: "hashicorp/aws", - VersionConstraint: "5.35.0", - }, - }, - Config: testAccRoleConfig_description(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "path", "/"), - resource.TestCheckResourceAttr(resourceName, "description", "This 1s a D3scr!pti0n with weird content: &@90ë\"'{«¡Çø}"), - ), - }, - { - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - // NOTE: Have to update docs to reflect this because giant issue - // Can't not use PlanOnly anymore with terraform-plugin-testing - // https://github.com/hashicorp/terraform-plugin-testing/issues/256 - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectEmptyPlan(), - }, - }, - Config: testAccRoleConfig_description(rName), - }, - }, - }) -} - func TestAccIAMRole_nameGenerated(t *testing.T) { ctx := acctest.Context(t) var conf iam.Role @@ -243,52 +202,6 @@ func TestAccIAMRole_namePrefix(t *testing.T) { }) } -// TODO: name prefix migration test -func TestAccIAMRole_MigrateFromPluginSDK_namePrefix(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - ExternalProviders: map[string]resource.ExternalProvider{ - "aws": { - Source: "hashicorp/aws", - VersionConstraint: "5.35.0", - }, - }, - Config: testAccRoleConfig_namePrefix(acctest.ResourcePrefix), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", acctest.ResourcePrefix), - resource.TestCheckResourceAttr(resourceName, "name_prefix", acctest.ResourcePrefix), - ), - }, - { - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - // NOTE: Have to update docs to reflect this because giant issue - // Can't not use PlanOnly anymore with terraform-plugin-testing - // https://github.com/hashicorp/terraform-plugin-testing/issues/256 - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectEmptyPlan(), - }, - }, - Config: testAccRoleConfig_namePrefix(acctest.ResourcePrefix), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", acctest.ResourcePrefix), - resource.TestCheckResourceAttr(resourceName, "name_prefix", acctest.ResourcePrefix), - ), - }, - }, - }) -} - func TestAccIAMRole_testNameChange(t *testing.T) { ctx := acctest.Context(t) var conf iam.Role @@ -633,46 +546,6 @@ func TestAccIAMRole_maxSessionDuration(t *testing.T) { }) } -func TestAccIAMRole_MigrateFromPluginSDK_MaxSessionDuration(t *testing.T) { - ctx := acctest.Context(t) - var conf iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - ExternalProviders: map[string]resource.ExternalProvider{ - "aws": { - Source: "hashicorp/aws", - VersionConstraint: "5.34.0", - }, - }, - Config: testAccRoleConfig_maxSessionDuration(rName, 3700), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "max_session_duration", "3700"), - ), - }, - { - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - // NOTE: Have to update docs to reflect this because giant issue - // Can't not use PlanOnly anymore with terraform-plugin-testing - // https://github.com/hashicorp/terraform-plugin-testing/issues/256 - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectEmptyPlan(), - }, - }, - Config: testAccRoleConfig_maxSessionDuration(rName, 3700), - }, - }, - }) -} - func TestAccIAMRole_permissionsBoundary(t *testing.T) { ctx := acctest.Context(t) var role iam.Role @@ -766,48 +639,6 @@ func TestAccIAMRole_permissionsBoundary(t *testing.T) { }) } -func TestAccIAMRole_MigrateFromPluginSDK_PermissionBoundary(t *testing.T) { - ctx := acctest.Context(t) - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - var role iam.Role - resourceName := "aws_iam_role.test" - permissionsBoundary1 := fmt.Sprintf("arn:%s:iam::aws:policy/AdministratorAccess", acctest.Partition()) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - ExternalProviders: map[string]resource.ExternalProvider{ - "aws": { - Source: "hashicorp/aws", - VersionConstraint: "5.34.0", - }, - }, - Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "permissions_boundary", permissionsBoundary1), - testAccCheckRolePermissionsBoundary(&role, permissionsBoundary1), - ), - }, - { - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - // NOTE: Have to update docs to reflect this because giant issue - // Can't not use PlanOnly anymore with terraform-plugin-testing - // https://github.com/hashicorp/terraform-plugin-testing/issues/256 - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectEmptyPlan(), - }, - }, - Config: testAccRoleConfig_permissionsBoundary(rName, permissionsBoundary1), - }, - }, - }) -} - func TestAccIAMRole_tags(t *testing.T) { ctx := acctest.Context(t) var role iam.Role @@ -1074,48 +905,6 @@ func TestAccIAMRole_ManagedPolicy_basic(t *testing.T) { }) } -func TestAccIAMRole_MigrateFromPluginSDK_ManagedPolicy(t *testing.T) { - ctx := acctest.Context(t) - var role iam.Role - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - policyName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_iam_role.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), - CheckDestroy: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - ExternalProviders: map[string]resource.ExternalProvider{ - "aws": { - Source: "hashicorp/aws", - VersionConstraint: "5.34.0", - }, - }, - Config: testAccRoleConfig_policyManaged(rName, policyName1), - Check: resource.ComposeTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &role), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"), - ), - }, - { - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - // NOTE: Have to update docs to reflect this because giant issue - // Can't not use PlanOnly anymore with terraform-plugin-testing - // https://github.com/hashicorp/terraform-plugin-testing/issues/256 - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectEmptyPlan(), - }, - }, - Config: testAccRoleConfig_policyManaged(rName, policyName1), - }, - }, - }) -} - func TestAccIAMRole_ManagedPolicy_badARN(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) From ad5c905a2da4329b35c6d2a5a468180eb4dd8930 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:56:26 -0500 Subject: [PATCH 099/118] some cleanup --- internal/service/iam/role.go | 81 ++++++++++++++---------------------- 1 file changed, 31 insertions(+), 50 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index e0454599ac3..481ccd1597c 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -97,23 +97,23 @@ func (m editPlanForSameReorderedPolicies) PlanModifyMap(ctx context.Context, req } // TODO: do this more official way? - plan_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, req.PlanValue) + planInlinePoliciesMap := flex.ExpandFrameworkStringValueMap(ctx, req.PlanValue) - if len(plan_inline_policies_map) == 0 { + if len(planInlinePoliciesMap) == 0 { return } - state_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, req.StateValue) + stateInlinePoliciesMap := flex.ExpandFrameworkStringValueMap(ctx, req.StateValue) // If policies match, set plan for policy to use state version so that we don't see if diff bc ordering does not matter - for name, plan_policy_doc := range plan_inline_policies_map { - if state_policy_doc, ok := state_inline_policies_map[name]; ok { - if verify.PolicyStringsEquivalent(plan_policy_doc, state_policy_doc) { - plan_inline_policies_map[name] = state_policy_doc + for name, planPolicyDoc := range planInlinePoliciesMap { + if statePolicyDoc, ok := stateInlinePoliciesMap[name]; ok { + if verify.PolicyStringsEquivalent(planPolicyDoc, statePolicyDoc) { + planInlinePoliciesMap[name] = statePolicyDoc } } } - resp.PlanValue = flex.FlattenFrameworkStringValueMap(ctx, plan_inline_policies_map) + resp.PlanValue = flex.FlattenFrameworkStringValueMap(ctx, planInlinePoliciesMap) } func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -182,17 +182,6 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest // validRolePolicyName, // ), // }, - // "policy": { - // Type: schema.TypeString, - // Optional: true, // semantically required but syntactically optional to allow empty inline_policy - // ValidateFunc: verify.ValidIAMPolicyJSON, - // DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, - // DiffSuppressOnRefresh: true, - // StateFunc: func(v interface{}) string { - // json, _ := verify.LegacyPolicyNormalize(v) - // return json - // }, - // }, }, "managed_policy_arns": schema.SetAttribute{ Optional: true, @@ -257,8 +246,6 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "permissions_boundary": schema.StringAttribute{ CustomType: fwtypes.ARNType, Optional: true, - // Computed: true, - // Default: stringdefault.StaticString(""), PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, @@ -303,7 +290,6 @@ func oldSDKRoleSchema(ctx context.Context) schema.Schema { }, "assume_role_policy": schema.StringAttribute{ Required: true, - // TODO Validate, }, "create_date": schema.StringAttribute{ Computed: true, @@ -320,7 +306,6 @@ func oldSDKRoleSchema(ctx context.Context) schema.Schema { Optional: true, Computed: true, Default: booldefault.StaticBool(false), - // TODO Default:false, }, "id": framework.IDAttribute(), "managed_policy_arns": schema.SetAttribute{ @@ -534,10 +519,6 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, output, err := retryCreateRole(ctx, conn, input) - // TODO: So this needs tags... do we need on resourceIamRoleData? - // if input.Tags != nil && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { - // input.Tags = nil - if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameRole, name, nil), @@ -765,8 +746,8 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res } var configPoliciesList []*iam.PutRolePolicyInput - inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) - configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), inline_policies_map) + inlinePoliciesMap := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) + configPoliciesList = expandRoleInlinePolicies(aws.StringValue(role.RoleName), inlinePoliciesMap) if !inlinePoliciesEquivalent(inlinePolicies, configPoliciesList) { state.InlinePolicies = flex.FlattenFrameworkStringValueMap(ctx, flattenRoleInlinePolicies(inlinePolicies)) @@ -911,45 +892,45 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, } if !plan.InlinePolicies.Equal(state.InlinePolicies) && inlinePoliciesActualDiff(ctx, &plan, &state) { - old_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) - new_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) + oldInlinePoliciesMap := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) + newInlinePoliciesMap := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) - var remove_policy_names []string - for k := range old_inline_policies_map { - if _, ok := new_inline_policies_map[k]; !ok { - remove_policy_names = append(remove_policy_names, k) + var removePolicyNames []string + for k := range oldInlinePoliciesMap { + if _, ok := newInlinePoliciesMap[k]; !ok { + removePolicyNames = append(removePolicyNames, k) } } // need set like object to store policy names we want to add - add_policy_names := make(map[string]int64) - for k, v := range new_inline_policies_map { - val, ok := old_inline_policies_map[k] + addPolicyNames := make(map[string]int64) + for k, v := range newInlinePoliciesMap { + val, ok := oldInlinePoliciesMap[k] // If the key exists if !ok { - add_policy_names[k] = 0 + addPolicyNames[k] = 0 continue } if !verify.PolicyStringsEquivalent(v, val) { - add_policy_names[k] = 0 + addPolicyNames[k] = 0 } } roleName := state.Name.ValueString() - nsPolicies := expandRoleInlinePolicies(roleName, new_inline_policies_map) + nsPolicies := expandRoleInlinePolicies(roleName, newInlinePoliciesMap) // getting policy objects we want to add based on add_policy_names map - var add_policies []*iam.PutRolePolicyInput + var addPolicies []*iam.PutRolePolicyInput for _, val := range nsPolicies { - if _, ok := add_policy_names[*val.PolicyName]; ok { - add_policies = append(add_policies, val) + if _, ok := addPolicyNames[*val.PolicyName]; ok { + addPolicies = append(addPolicies, val) } else { } } // Always add before delete - if err := r.addRoleInlinePolicies(ctx, add_policies); err != nil { + if err := r.addRoleInlinePolicies(ctx, addPolicies); err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.InlinePolicies.String(), err), err.Error(), @@ -957,7 +938,7 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, return } - if err := deleteRoleInlinePolicies(ctx, conn, roleName, remove_policy_names); err != nil { + if err := deleteRoleInlinePolicies(ctx, conn, roleName, removePolicyNames); err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionUpdating, state.ID.String(), plan.InlinePolicies.String(), err), err.Error(), @@ -1359,11 +1340,11 @@ func (r resourceIamRole) addRoleInlinePolicies(ctx context.Context, policies []* func inlinePoliciesActualDiff(ctx context.Context, plan *resourceIamRoleData, state *resourceIamRoleData) bool { roleName := state.Name.ValueString() - old_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) - new_inline_policies_map := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) + oldInlinePoliciesMap := flex.ExpandFrameworkStringValueMap(ctx, state.InlinePolicies) + newInlinePoliciesMap := flex.ExpandFrameworkStringValueMap(ctx, plan.InlinePolicies) - osPolicies := expandRoleInlinePolicies(roleName, old_inline_policies_map) - nsPolicies := expandRoleInlinePolicies(roleName, new_inline_policies_map) + osPolicies := expandRoleInlinePolicies(roleName, oldInlinePoliciesMap) + nsPolicies := expandRoleInlinePolicies(roleName, newInlinePoliciesMap) return !inlinePoliciesEquivalent(nsPolicies, osPolicies) } From a91a3fe61ab62f2dfaceceaca1a36419568ba572 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 16:01:06 -0500 Subject: [PATCH 100/118] cleanup --- internal/service/iam/tags.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/service/iam/tags.go b/internal/service/iam/tags.go index 1b69528b4d0..fef0ebb1047 100644 --- a/internal/service/iam/tags.go +++ b/internal/service/iam/tags.go @@ -153,9 +153,6 @@ func policyCreateTags(ctx context.Context, conn iamiface.IAMAPI, identifier stri func roleUpdateTags(ctx context.Context, conn iamiface.IAMAPI, identifier string, oldTagsMap, newTagsMap any) error { oldTags := tftags.New(ctx, oldTagsMap) newTags := tftags.New(ctx, newTagsMap) - fmt.Println("In update role tags") - fmt.Println(fmt.Sprintf("Old tags: %+v", oldTags)) - fmt.Println(fmt.Sprintf("Old tags: %+v", newTags)) if removedTags := oldTags.Removed(newTags).IgnoreSystem(names.IAM); len(removedTags) > 0 { input := &iam.UntagRoleInput{ From 8a8ffc89a008d70303d799f39befad2ee60cae8d Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 16:06:49 -0500 Subject: [PATCH 101/118] test fix --- .../iam/role_policy_attachment_test.go | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/internal/service/iam/role_policy_attachment_test.go b/internal/service/iam/role_policy_attachment_test.go index 3f0d6071098..aa4d1892362 100644 --- a/internal/service/iam/role_policy_attachment_test.go +++ b/internal/service/iam/role_policy_attachment_test.go @@ -98,33 +98,32 @@ func TestAccIAMRolePolicyAttachment_disappears(t *testing.T) { }) } -// TODO: fix later -// func TestAccIAMRolePolicyAttachment_Disappears_role(t *testing.T) { -// ctx := acctest.Context(t) -// roleName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_iam_role_policy_attachment.test1" -// iamRoleResourceName := "aws_iam_role.test" - -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckRolePolicyAttachmentDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccRolePolicyAttachmentConfig_attach(roleName, policyName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckRolePolicyAttachmentExists(ctx, resourceName), -// // DeleteConflict: Cannot delete entity, must detach all policies first. -// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRolePolicyAttachment(), resourceName), -// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), iamRoleResourceName), -// ), -// ExpectNonEmptyPlan: true, -// }, -// }, -// }) -// } +func TestAccIAMRolePolicyAttachment_Disappears_role(t *testing.T) { + ctx := acctest.Context(t) + roleName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + policyName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_role_policy_attachment.test1" + iamRoleResourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckRolePolicyAttachmentDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccRolePolicyAttachmentConfig_attach(roleName, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRolePolicyAttachmentExists(ctx, resourceName), + // DeleteConflict: Cannot delete entity, must detach all policies first. + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRolePolicyAttachment(), resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole, iamRoleResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} func testAccCheckRolePolicyAttachmentDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { From 006b4aaf9929fdd02b4d8b7ba7c09dee13df6c81 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 16:07:54 -0500 Subject: [PATCH 102/118] another test --- internal/service/iam/instance_profile_test.go | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/internal/service/iam/instance_profile_test.go b/internal/service/iam/instance_profile_test.go index 60f2eb2f700..79c106fe3d9 100644 --- a/internal/service/iam/instance_profile_test.go +++ b/internal/service/iam/instance_profile_test.go @@ -206,30 +206,31 @@ func TestAccIAMInstanceProfile_disappears(t *testing.T) { }) } -// TODO: fix this later -// func TestAccIAMInstanceProfile_Disappears_role(t *testing.T) { -// ctx := acctest.Context(t) -// var conf iam.InstanceProfile -// resourceName := "aws_iam_instance_profile.test" -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckInstanceProfileDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccInstanceProfileConfig_basic(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckInstanceProfileExists(ctx, resourceName, &conf), -// acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole(), "aws_iam_role.test"), -// ), -// ExpectNonEmptyPlan: true, -// }, -// }, -// }) -// } +func TestAccIAMInstanceProfile_Disappears_role(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.InstanceProfile + resourceName := "aws_iam_instance_profile.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckInstanceProfileDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccInstanceProfileConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceProfileExists(ctx, resourceName, &conf), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole, "aws_iam_role.test"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +// acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole, iamRoleResourceName), func testAccCheckInstanceProfileDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { From fcabcd4bc3bed982f9caba404ae58f2fadb60173 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 16:14:19 -0500 Subject: [PATCH 103/118] working on getting cluster role association test working --- .../rds/cluster_role_association_test.go | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/internal/service/rds/cluster_role_association_test.go b/internal/service/rds/cluster_role_association_test.go index fd3c173aa46..e25b37292ca 100644 --- a/internal/service/rds/cluster_role_association_test.go +++ b/internal/service/rds/cluster_role_association_test.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" // TODO: bring back later - // "github.com/hashicorp/terraform-provider-aws/internal/service/iam" + tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam" tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) @@ -101,31 +101,33 @@ func TestAccRDSClusterRoleAssociation_Disappears_cluster(t *testing.T) { }) } -// TODO: fix later -// func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) { -// ctx := acctest.Context(t) -// var dbClusterRole rds.DBClusterRole -// rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) -// resourceName := "aws_rds_cluster_role_association.test" -// roleResourceName := "aws_iam_role.test" - -// resource.ParallelTest(t, resource.TestCase{ -// PreCheck: func() { acctest.PreCheck(ctx, t) }, -// ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, -// CheckDestroy: testAccCheckClusterRoleAssociationDestroy(ctx), -// Steps: []resource.TestStep{ -// { -// Config: testAccClusterRoleAssociationConfig_basic(rName), -// Check: resource.ComposeTestCheckFunc( -// testAccCheckClusterRoleAssociationExists(ctx, resourceName, &dbClusterRole), -// acctest.CheckResourceDisappears(ctx, acctest.Provider, iam.ResourceRole(), roleResourceName), -// ), -// ExpectNonEmptyPlan: true, -// }, -// }, -// }) -// } +func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) { + ctx := acctest.Context(t) + var dbClusterRole rds.DBClusterRole + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_rds_cluster_role_association.test" + roleResourceName := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckClusterRoleAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccClusterRoleAssociationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterRoleAssociationExists(ctx, resourceName, &dbClusterRole), + // acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, iam.ResourceRole, roleResourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole, roleResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +// acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole, resourceName), func testAccCheckClusterRoleAssociationExists(ctx context.Context, resourceName string, v *rds.DBClusterRole) resource.TestCheckFunc { return func(s *terraform.State) error { From c30adeeb7703e1eac873fd810e43124ff5522bf9 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:02:00 -0500 Subject: [PATCH 104/118] more cleanup --- internal/service/iam/exports_test.go | 2 +- internal/service/iam/role.go | 2 +- internal/service/iam/service_package_gen.go | 2 +- internal/service/rds/cluster_role_association_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/iam/exports_test.go b/internal/service/iam/exports_test.go index 7260aa9a648..07c6f141a5e 100644 --- a/internal/service/iam/exports_test.go +++ b/internal/service/iam/exports_test.go @@ -5,7 +5,7 @@ package iam // Exports for use in tests only. var ( - ResourceRole = newResourceRole + ResourceRole = NewResourceRole ResourceGroupPolicyAttachment = resourceGroupPolicyAttachment ResourcePolicyAttachment = resourcePolicyAttachment ResourceRolePolicyAttachment = resourceRolePolicyAttachment diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 481ccd1597c..7a118c72483 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -52,7 +52,7 @@ const ( // @FrameworkResource(name="Role") // @Tags(identifierAttribute="id") -func newResourceRole(_ context.Context) (resource.ResourceWithConfigure, error) { +func NewResourceRole(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourceIamRole{} r.SetMigratedFromPluginSDK(true) diff --git a/internal/service/iam/service_package_gen.go b/internal/service/iam/service_package_gen.go index 68466c67962..020294dd505 100644 --- a/internal/service/iam/service_package_gen.go +++ b/internal/service/iam/service_package_gen.go @@ -22,7 +22,7 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { return []*types.ServicePackageFrameworkResource{ { - Factory: newResourceRole, + Factory: NewResourceRole, Name: "Role", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: "id", diff --git a/internal/service/rds/cluster_role_association_test.go b/internal/service/rds/cluster_role_association_test.go index e25b37292ca..6e322bad26c 100644 --- a/internal/service/rds/cluster_role_association_test.go +++ b/internal/service/rds/cluster_role_association_test.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" // TODO: bring back later - tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam" + tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam" tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) From 1db6d75867875c7defaf82cf5fa99e255f4c5c77 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:00:42 -0500 Subject: [PATCH 105/118] more updates --- internal/service/iam/role.go | 41 ++++++------------- .../rds/cluster_role_association_test.go | 2 +- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 7a118c72483..8699fc9da18 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -10,13 +10,14 @@ import ( "net/url" "time" - // "github.com/YakDriver/regexache" + "github.com/YakDriver/regexache" "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" awspolicy "github.com/hashicorp/awspolicyequivalence" "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -67,36 +68,30 @@ func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataR response.TypeName = "aws_iam_role" } -// TODO: move this to Modify plan if both aren't empty +// As this is map and logic a little more complex, felt appropriate to make it's own planmodifier func EditPlanForSameReorderedPolicies() planmodifier.Map { return editPlanForSameReorderedPolicies{} } type editPlanForSameReorderedPolicies struct{} -// TODO: edit this once we get working func (m editPlanForSameReorderedPolicies) Description(_ context.Context) string { - return "Once set, the value of this attribute in state will not change." + return "If plan and state of inline policy is the same equivalent policy, do not include in plan" } -// TODO: edit this once we get working func (m editPlanForSameReorderedPolicies) MarkdownDescription(_ context.Context) string { - return "Once set, the value of this attribute in state will not change." + return "If plan and state of inline policy is the same equivalent policy, do not include in plan" } -// TODO: move to modify plan?? func (m editPlanForSameReorderedPolicies) PlanModifyMap(ctx context.Context, req planmodifier.MapRequest, resp *planmodifier.MapResponse) { - // Do nothing if there is no state value. if req.PlanValue.IsUnknown() || req.PlanValue.IsNull() { return } - // TODO: something with making sure not just unknown but has value if req.StateValue.IsUnknown() || req.StateValue.IsNull() { return } - // TODO: do this more official way? planInlinePoliciesMap := flex.ExpandFrameworkStringValueMap(ctx, req.PlanValue) if len(planInlinePoliciesMap) == 0 { @@ -133,6 +128,7 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Required: true, CustomType: fwtypes.IAMPolicyType, // TODO: possible plan validator? Or normalize what is going into state + // TODO: should add test case as well to validate // DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, // DiffSuppressOnRefresh: true, // StateFunc: func(v interface{}) string { @@ -164,24 +160,16 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Computed: true, Default: booldefault.StaticBool(false), }, - // TODO: maybe mapof of IAMPolicytype? "inline_policies": schema.MapAttribute{ ElementType: fwtypes.IAMPolicyType, Optional: true, PlanModifiers: []planmodifier.Map{ EditPlanForSameReorderedPolicies(), - // TODO: custom plan modifier for something like editing plan is fine }, - // TODO: custom validator for name stuff? - // TODO: validators and name func for both - // "name": { - // Type: schema.TypeString, - // Optional: true, // semantically required but syntactically optional to allow empty inline_policy - // ValidateFunc: validation.All( - // validation.StringIsNotEmpty, - // validRolePolicyName, - // ), - // }, + Validators: []validator.Map{ + mapvalidator.KeysAre(stringvalidator.LengthBetween(1, rolePolicyNameMaxLen)), + mapvalidator.KeysAre(stringvalidator.RegexMatches(regexache.MustCompile(`^[\w+=,.@-]*$`), "must match [\\w+=,.@-]")), + }, }, "managed_policy_arns": schema.SetAttribute{ Optional: true, @@ -210,10 +198,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest }, Validators: []validator.String{ stringvalidator.LengthAtMost(roleNameMaxLen), - // TODO: uncomment when ready - // stringvalidator.ConflictsWith( - // path.MatchRelative().AtParent().AtName("name_prefix"), - // ), + stringvalidator.ConflictsWith( + path.MatchRelative().AtParent().AtName("name_prefix"), + ), }, }, "name_prefix": schema.StringAttribute{ @@ -235,10 +222,8 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Default: stringdefault.StaticString("/"), PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplaceIfConfigured(), - // TODO: can I do this and remove setting in Update/read? stringplanmodifier.UseStateForUnknown(), }, - // Default: stringdefault.StaticString("/"), Validators: []validator.String{ stringvalidator.LengthBetween(0, 512), }, diff --git a/internal/service/rds/cluster_role_association_test.go b/internal/service/rds/cluster_role_association_test.go index 6e322bad26c..d067424a819 100644 --- a/internal/service/rds/cluster_role_association_test.go +++ b/internal/service/rds/cluster_role_association_test.go @@ -119,7 +119,7 @@ func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckClusterRoleAssociationExists(ctx, resourceName, &dbClusterRole), // acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, iam.ResourceRole, roleResourceName), - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole, roleResourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.NewResourceRole, roleResourceName), ), ExpectNonEmptyPlan: true, }, From 939e4fe53fbb41d4d4c906c09f5901fdd8082d83 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:05:31 -0500 Subject: [PATCH 106/118] another update --- internal/service/iam/role.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 8699fc9da18..33c6c3a07fa 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -127,14 +127,6 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "assume_role_policy": schema.StringAttribute{ Required: true, CustomType: fwtypes.IAMPolicyType, - // TODO: possible plan validator? Or normalize what is going into state - // TODO: should add test case as well to validate - // DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, - // DiffSuppressOnRefresh: true, - // StateFunc: func(v interface{}) string { - // json, _ := structure.NormalizeJsonString(v) - // return json - // }, }, "create_date": schema.StringAttribute{ Computed: true, @@ -629,6 +621,10 @@ func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.Modif response.Diagnostics.Append(response.Plan.SetAttribute(ctx, path.Root("description"), state.Description)...) } + if state.AssumeRolePolicy.ValueString() == plan.AssumeRolePolicy.ValueString() { + response.Diagnostics.Append(response.Plan.SetAttribute(ctx, path.Root("assume_role_policy"), state.AssumeRolePolicy)...) + } + if state.NamePrefix.ValueString() == plan.NamePrefix.ValueString() { response.Diagnostics.Append(response.Plan.SetAttribute(ctx, path.Root("name_prefix"), state.NamePrefix)...) } From d2c92dca77b4d52a4ada9135e4e20fc1cbfd35d3 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:25:03 -0500 Subject: [PATCH 107/118] another validator --- internal/service/iam/role.go | 51 ++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 33c6c3a07fa..d381152ba84 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "net/url" + "regexp" "time" "github.com/YakDriver/regexache" @@ -16,6 +17,7 @@ import ( "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" awspolicy "github.com/hashicorp/awspolicyequivalence" "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -68,6 +70,48 @@ func (r *resourceIamRole) Metadata(_ context.Context, request resource.MetadataR response.TypeName = "aws_iam_role" } +// TODO: should stringvalidator have something like `RegexNotMatches`? Making custom one for now that's opposite of given one +// From terraform-plugin-framework, just the opposite implementation +// https://github.com/hashicorp/terraform-plugin-framework-validators/blob/main/stringvalidator/regex_matches.go +func RegexNotMatches(regexp *regexp.Regexp, message string) validator.String { + return regexNotMatchesValidator{ + regexp: regexp, + message: message, + } +} + +type regexNotMatchesValidator struct { + regexp *regexp.Regexp + message string +} + +// Description describes the validation in plain text formatting. +func (validator regexNotMatchesValidator) Description(_ context.Context) string { + return fmt.Sprintf("value must not match regular expression '%s'", validator.regexp) +} + +// MarkdownDescription describes the validation in Markdown formatting. +func (validator regexNotMatchesValidator) MarkdownDescription(ctx context.Context) string { + return validator.Description(ctx) +} + +// Validate performs the validation. +func (v regexNotMatchesValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) { + if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() { + return + } + + value := request.ConfigValue.ValueString() + + if v.regexp.MatchString(value) { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic( + request.Path, + v.Description(ctx), + value, + )) + } +} + // As this is map and logic a little more complex, felt appropriate to make it's own planmodifier func EditPlanForSameReorderedPolicies() planmodifier.Map { return editPlanForSameReorderedPolicies{} @@ -140,12 +184,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Default: stringdefault.StaticString(""), Validators: []validator.String{ stringvalidator.LengthBetween(0, 1000), + stringvalidator.RegexMatches(regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`), + RegexNotMatches(regexache.MustCompile("[“‘]"), "cannot contain specially formatted single or double quotes: [“‘]"), }, - // TODO: need to add validators and test - // ValidateFunc: validation.All( - // validation.StringDoesNotMatch(regexache.MustCompile("[“‘]"), "cannot contain specially formatted single or double quotes: [“‘]"), - // validation.StringMatch(regexache.MustCompile(`[\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*`), `must satisfy regular expression pattern: [\p{L}\p{M}\p{Z}\p{S}\p{N}\p{P}]*)`), - // ), }, "force_detach_policies": schema.BoolAttribute{ Optional: true, From cdb52f54287f3faabdc08b907b808a3bc5758188 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:40:40 -0500 Subject: [PATCH 108/118] more cleanup --- internal/service/iam/role.go | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index d381152ba84..12e5e7e9666 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -25,6 +25,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" @@ -192,6 +193,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest Optional: true, Computed: true, Default: booldefault.StaticBool(false), + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, }, "inline_policies": schema.MapAttribute{ ElementType: fwtypes.IAMPolicyType, @@ -368,19 +372,9 @@ func oldSDKRoleSchema(ctx context.Context) schema.Schema { }, "permissions_boundary": schema.StringAttribute{ Optional: true, - // TODO Validate, - }, - "tags": // TODO tftags.TagsAttribute() - schema.MapAttribute{ - ElementType: types.StringType, - Optional: true, - }, - "tags_all": // TODO tftags.TagsAttributeComputedOnly() - schema.MapAttribute{ - ElementType: types.StringType, - Optional: true, - Computed: true, }, + "tags": tftags.TagsAttribute(), + "tags_all": tftags.TagsAttributeComputedOnly(), "unique_id": schema.StringAttribute{ Computed: true, }, @@ -391,11 +385,9 @@ func oldSDKRoleSchema(ctx context.Context) schema.Schema { Attributes: map[string]schema.Attribute{ "name": schema.StringAttribute{ Optional: true, - // TODO Validate, }, "policy": schema.StringAttribute{ Optional: true, - // TODO Validate, }, }, }, @@ -415,7 +407,6 @@ func (r *resourceIamRole) UpgradeState(ctx context.Context) map[int64]resource.S } } -// TODO: ok finish working on this to perform upgrade cleanly func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { type resourceIamRoleDataV0 struct { ARN types.String `tfsdk:"arn"` @@ -725,9 +716,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res state.UniqueID = flex.StringToFramework(ctx, role.RoleId) if state.ForceDetachPolicies.IsNull() { - // TODO: better way to do this that is more framework friendly? - temp := false - state.ForceDetachPolicies = flex.BoolToFramework(ctx, &temp) + state.ForceDetachPolicies = types.BoolValue(false) } if role.PermissionsBoundary != nil { From 9122382ee1b8dc1f540f30d8f5d9a588e6cca87c Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:57:22 -0500 Subject: [PATCH 109/118] more cleanup --- internal/service/iam/role.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 12e5e7e9666..820b18b8615 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -485,8 +485,6 @@ func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.Upgrade resp.Diagnostics.Append(diags...) } -// TODO: maybe refreshFromOutput - func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { conn := r.Meta().IAMConn(ctx) @@ -570,7 +568,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, if tags := getTagsIn(ctx); input.Tags == nil && len(tags) > 0 { err := roleCreateTags(ctx, conn, name, tags) - // TODO: read errors or something + // TODO: not sure how to convert this to framework // If default tags only, continue. Otherwise, error. // if v, ok := d.GetOk(names.AttrTags); (!ok || len(v.(map[string]interface{})) == 0) && errs.IsUnsupportedOperationInPartitionError(conn.PartitionID, err) { // return append(diags, resourceRoleRead(ctx, d, meta)...) @@ -617,10 +615,6 @@ func (r resourceIamRole) Delete(ctx context.Context, req resource.DeleteRequest, err := DeleteRole(ctx, conn, state.Name.ValueString(), state.ForceDetachPolicies.ValueBool(), hasInline, hasManaged) if err != nil { - // TODO: do something like this to skip deletes on roles that are gone? - // if err.IsA[*awstypes.ResourceNotFoundException](err) { - // return - // } resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionDeleting, state.Name.String(), state.ARN.String(), err), err.Error(), @@ -679,7 +673,7 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res return FindRoleByName(ctx, conn, state.ID.ValueString()) }, true) - // NOTE: Same issue here, I left old conditional here as example, not sure what else can/should be done + // NOTE: Same issue here, left old conditional here as example, not sure what else can/should be done // if !d.IsNewResource() && tfresource.NotFound(err) { if tfresource.NotFound(err) { resp.State.RemoveResource(ctx) From 83d9b62b6f1fead780a05797add75cdd0794fa29 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 20:02:55 -0500 Subject: [PATCH 110/118] cleanup test print messages --- internal/service/iam/role_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index c5fd126ce07..8d259410333 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -618,7 +618,6 @@ func TestAccIAMRole_permissionsBoundary(t *testing.T) { PreConfig: func() { // delete the boundary manually conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx) - fmt.Println(fmt.Sprintf("test role name: %s", *role.RoleName)) input := &iam.DeleteRolePermissionsBoundaryInput{ RoleName: role.RoleName, } @@ -1242,9 +1241,6 @@ func testAccCheckRoleExists(ctx context.Context, n string, v *iam.Role) resource return fmt.Errorf("No IAM Role ID is set") } - // TODO: Debugging - fmt.Println(fmt.Sprintf("testAccCheckRoleExists ID: %s", rs.Primary.ID)) - conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn(ctx) output, err := tfiam.FindRoleByName(ctx, conn, rs.Primary.ID) From 5f8fb30c81b6594e45e5f238cad2e964cc8d7b20 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 22:55:23 -0500 Subject: [PATCH 111/118] managed policy test --- internal/service/iam/role.go | 8 ++++++-- internal/service/iam/role_test.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 820b18b8615..44c8553245d 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -21,6 +21,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -769,9 +770,12 @@ func (r resourceIamRole) Read(ctx context.Context, req resource.ReadRequest, res ) return } - state.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) + if len(policyARNs) == 0 { + state.ManagedPolicyArns = types.SetValueMust(fwtypes.ARNType, []attr.Value{}) + } else { + state.ManagedPolicyArns = flex.FlattenFrameworkStringValueSet(ctx, policyARNs) + } } - setTagsOut(ctx, role.Tags) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index 8d259410333..e72748d71ff 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -2665,7 +2665,7 @@ resource "aws_iam_role" "test" { }] }) - managed_policy_arns = ["arn:aws:iam::aws:policy/AdministratorAccess-Amplify"] + managed_policy_arns = [] } resource "aws_iam_policy" "managed-policy1" { From 3350161ab9c4d240bad81765edfeee7cd407bcce Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 23:10:24 -0500 Subject: [PATCH 112/118] cleanup --- internal/service/iam/role.go | 18 +++++++++--------- .../rds/cluster_role_association_test.go | 4 ---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 44c8553245d..1903c364e18 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -88,13 +88,13 @@ type regexNotMatchesValidator struct { } // Description describes the validation in plain text formatting. -func (validator regexNotMatchesValidator) Description(_ context.Context) string { - return fmt.Sprintf("value must not match regular expression '%s'", validator.regexp) +func (v regexNotMatchesValidator) Description(_ context.Context) string { + return fmt.Sprintf("value must not match regular expression '%s'", v.regexp) } // MarkdownDescription describes the validation in Markdown formatting. -func (validator regexNotMatchesValidator) MarkdownDescription(ctx context.Context) string { - return validator.Description(ctx) +func (v regexNotMatchesValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) } // Validate performs the validation. @@ -304,7 +304,7 @@ type resourceIamRoleData struct { TagsAll types.Map `tfsdk:"tags_all"` } -func oldSDKRoleSchema(ctx context.Context) schema.Schema { +func oldSDKRoleSchema() schema.Schema { return schema.Schema{ Version: 0, Attributes: map[string]schema.Attribute{ @@ -398,17 +398,17 @@ func oldSDKRoleSchema(ctx context.Context) schema.Schema { } func (r *resourceIamRole) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { - schemaV0 := oldSDKRoleSchema(ctx) + schemaV0 := oldSDKRoleSchema() return map[int64]resource.StateUpgrader{ 0: { PriorSchema: &schemaV0, - StateUpgrader: upgradeIAMRoleResourceStateV0toV1, + StateUpgrader: upgradeRoleResourceStateV0toV1, }, } } -func upgradeIAMRoleResourceStateV0toV1(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { +func upgradeRoleResourceStateV0toV1(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { type resourceIamRoleDataV0 struct { ARN types.String `tfsdk:"arn"` AssumeRolePolicy types.String `tfsdk:"assume_role_policy"` @@ -499,7 +499,7 @@ func (r resourceIamRole) Create(ctx context.Context, req resource.CreateRequest, if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.IAM, create.ErrActionCreating, ResNameRole, plan.AssumeRolePolicy.String(), nil), - errors.New(fmt.Sprintf("assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err)).Error(), + fmt.Errorf("assume_role_policy (%s) is invalid JSON: %s", assumeRolePolicy, err).Error(), ) return } diff --git a/internal/service/rds/cluster_role_association_test.go b/internal/service/rds/cluster_role_association_test.go index d067424a819..df7b760a814 100644 --- a/internal/service/rds/cluster_role_association_test.go +++ b/internal/service/rds/cluster_role_association_test.go @@ -14,7 +14,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - // TODO: bring back later tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam" tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -118,7 +117,6 @@ func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) { Config: testAccClusterRoleAssociationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterRoleAssociationExists(ctx, resourceName, &dbClusterRole), - // acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, iam.ResourceRole, roleResourceName), acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.NewResourceRole, roleResourceName), ), ExpectNonEmptyPlan: true, @@ -127,8 +125,6 @@ func TestAccRDSClusterRoleAssociation_Disappears_role(t *testing.T) { }) } -// acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfiam.ResourceRole, resourceName), - func testAccCheckClusterRoleAssociationExists(ctx context.Context, resourceName string, v *rds.DBClusterRole) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] From 553808ad1c3ffb77549430e3cdff36c37fe57694 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Sun, 4 Feb 2024 23:49:26 -0500 Subject: [PATCH 113/118] golangci fixies --- internal/service/iam/role.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 1903c364e18..8a31b697717 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -50,9 +50,11 @@ import ( ) const ( - roleNameMaxLen = 64 - roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength - ResNameRole = "IAM Role" + roleNameMaxLen = 64 + roleNamePrefixMaxLen = roleNameMaxLen - id.UniqueIDSuffixLength + ResNameRole = "IAM Role" + MaxSessionDurationMin = 3600 + MaxSessionDurationMax = 43200 ) // @FrameworkResource(name="Role") @@ -219,9 +221,9 @@ func (r *resourceIamRole) Schema(ctx context.Context, req resource.SchemaRequest "max_session_duration": schema.Int64Attribute{ Optional: true, Computed: true, - Default: int64default.StaticInt64(3600), + Default: int64default.StaticInt64(MaxSessionDurationMin), Validators: []validator.Int64{ - int64validator.Between(3600, 43200), + int64validator.Between(MaxSessionDurationMin, MaxSessionDurationMax), }, PlanModifiers: []planmodifier.Int64{ int64planmodifier.UseStateForUnknown(), @@ -655,7 +657,6 @@ func (r *resourceIamRole) ModifyPlan(ctx context.Context, request resource.Modif if state.NamePrefix.ValueString() == plan.NamePrefix.ValueString() { response.Diagnostics.Append(response.Plan.SetAttribute(ctx, path.Root("name_prefix"), state.NamePrefix)...) } - } r.SetTagsAll(ctx, request, response) } @@ -934,7 +935,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, for _, val := range nsPolicies { if _, ok := addPolicyNames[*val.PolicyName]; ok { addPolicies = append(addPolicies, val) - } else { } } @@ -954,7 +954,6 @@ func (r resourceIamRole) Update(ctx context.Context, req resource.UpdateRequest, ) return } - } if !plan.ManagedPolicyArns.Equal(state.ManagedPolicyArns) { From fb9bbc96929c6c9a967f28f769dc66cfeab403bc Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:48:28 -0500 Subject: [PATCH 114/118] update docs --- website/docs/r/iam_role.html.markdown | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/website/docs/r/iam_role.html.markdown b/website/docs/r/iam_role.html.markdown index bc37ca990ff..acde8720aa7 100644 --- a/website/docs/r/iam_role.html.markdown +++ b/website/docs/r/iam_role.html.markdown @@ -12,7 +12,7 @@ Provides an IAM role. ~> **NOTE:** If policies are attached to the role via the [`aws_iam_policy_attachment` resource](/docs/providers/aws/r/iam_policy_attachment.html) and you are modifying the role `name` or `path`, the `force_detach_policies` argument must be set to `true` and applied before attempting the operation otherwise you will encounter a `DeleteConflict` error. The [`aws_iam_role_policy_attachment` resource (recommended)](/docs/providers/aws/r/iam_role_policy_attachment.html) does not have this requirement. -~> **NOTE:** If you use this resource's `managed_policy_arns` argument or `inline_policy` configuration blocks, this resource will take over exclusive management of the role's respective policy types (e.g., both policy types if both arguments are used). These arguments are incompatible with other ways of managing a role's policies, such as [`aws_iam_policy_attachment`](/docs/providers/aws/r/iam_policy_attachment.html), [`aws_iam_role_policy_attachment`](/docs/providers/aws/r/iam_role_policy_attachment.html), and [`aws_iam_role_policy`](/docs/providers/aws/r/iam_role_policy.html). If you attempt to manage a role's policies by multiple means, you will get resource cycling and/or errors. +~> **NOTE:** If you use this resource's `managed_policy_arns` argument or `inline_policies` configuration blocks, this resource will take over exclusive management of the role's respective policy types (e.g., both policy types if both arguments are used). These arguments are incompatible with other ways of managing a role's policies, such as [`aws_iam_policy_attachment`](/docs/providers/aws/r/iam_policy_attachment.html), [`aws_iam_role_policy_attachment`](/docs/providers/aws/r/iam_role_policy_attachment.html), and [`aws_iam_role_policy`](/docs/providers/aws/r/iam_role_policy.html). If you attempt to manage a role's policies by multiple means, you will get resource cycling and/or errors. ## Example Usage @@ -74,10 +74,8 @@ resource "aws_iam_role" "example" { name = "yak_role" assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json # (not shown) - inline_policy { - name = "my_inline_policy" - - policy = jsonencode({ + inline_policies = { + "my_inline_policy" = jsonencode({ Version = "2012-10-17" Statement = [ { @@ -89,9 +87,8 @@ resource "aws_iam_role" "example" { }) } - inline_policy { - name = "policy-8675309" - policy = data.aws_iam_policy_document.inline_policy.json + inline_policies = { + "policy-8675309" = data.aws_iam_policy_document.inline_policy.json } } @@ -105,14 +102,14 @@ data "aws_iam_policy_document" "inline_policy" { ### Example of Removing Inline Policies -This example creates an IAM role with what appears to be empty IAM `inline_policy` argument instead of using `inline_policy` as a configuration block. The result is that if someone were to add an inline policy out-of-band, on the next apply, Terraform will remove that policy. +This example creates an IAM role with what appears to be empty IAM `inline_policies` argument instead of using `inline_policies` as a configuration block. The result is that if someone were to add an inline policy out-of-band, on the next apply, Terraform will remove that policy. ```terraform resource "aws_iam_role" "example" { name = "yak_role" assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json # (not shown) - inline_policy {} + inline_policies = {} } ``` @@ -182,7 +179,7 @@ The following arguments are optional: * `description` - (Optional) Description of the role. * `force_detach_policies` - (Optional) Whether to force detaching any policies the role has before destroying it. Defaults to `false`. -* `inline_policy` - (Optional) Configuration block defining an exclusive set of IAM inline policies associated with the IAM role. See below. If no blocks are configured, Terraform will not manage any inline policies in this resource. Configuring one empty block (i.e., `inline_policy {}`) will cause Terraform to remove _all_ inline policies added out of band on `apply`. +* `inline_policies` - (Optional) Configuration map defining an exclusive set of IAM inline policies associated with the IAM role. See below. If no blocks are configured, Terraform will not manage any inline policies in this resource. Configuring one empty block (i.e., `inline_policies = {}`) will cause Terraform to remove _all_ inline policies added out of band on `apply`. * `managed_policy_arns` - (Optional) Set of exclusive IAM managed policy ARNs to attach to the IAM role. If this attribute is not configured, Terraform will ignore policy attachments to this resource. When configured, Terraform will align the role's managed policy attachments with this set by attaching or detaching managed policies. Configuring an empty set (i.e., `managed_policy_arns = []`) will cause Terraform to remove _all_ managed policy attachments. * `max_session_duration` - (Optional) Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours. * `name` - (Optional, Forces new resource) Friendly name of the role. If omitted, Terraform will assign a random, unique name. See [IAM Identifiers](https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) for more information. @@ -191,14 +188,14 @@ The following arguments are optional: * `permissions_boundary` - (Optional) ARN of the policy that is used to set the permissions boundary for the role. * `tags` - Key-value mapping of tags for the IAM role. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. -### inline_policy +### inline_policies This configuration block supports the following: -~> **NOTE:** Since one empty block (i.e., `inline_policy {}`) is valid syntactically to remove out of band policies on `apply`, `name` and `policy` are technically _optional_. However, they are both _required_ in order to manage actual inline policies. Not including one or the other may not result in Terraform errors but will result in unpredictable and incorrect behavior. +~> **NOTE:** Since one empty block (i.e., `inline_policies = {}`) is valid syntactically to remove out of band policies on `apply`, `name` and `policy` are technically _optional_. However, they are both _required_ in order to manage actual inline policies. Not including one or the other may not result in Terraform errors but will result in unpredictable and incorrect behavior. -* `name` - (Required) Name of the role policy. -* `policy` - (Required) Policy document as a JSON formatted string. For more information about building IAM policy documents with Terraform, see the [AWS IAM Policy Document Guide](https://learn.hashicorp.com/tutorials/terraform/aws-iam-policy). +For each policy in the `inline_policies` map the key is the name of the role policy and hte value is Policy document as a JSON formatted string. For more information about building IAM policy documents with Terraform, see the [AWS IAM Policy Document Guide](https://learn.hashicorp.com/tutorials/terraform/aws-iam-policy). +Both are required for each inline policy entry ## Attribute Reference From df74c15afa662fbd011843f23c82e9fd29ce4194 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:08:01 -0500 Subject: [PATCH 115/118] website fix --- website/docs/r/iam_role.html.markdown | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/website/docs/r/iam_role.html.markdown b/website/docs/r/iam_role.html.markdown index acde8720aa7..89ac139116a 100644 --- a/website/docs/r/iam_role.html.markdown +++ b/website/docs/r/iam_role.html.markdown @@ -85,11 +85,9 @@ resource "aws_iam_role" "example" { }, ] }) + "policy-8675309" = data.aws_iam_policy_document.inline_policy.json } - inline_policies = { - "policy-8675309" = data.aws_iam_policy_document.inline_policy.json - } } data "aws_iam_policy_document" "inline_policy" { From 8b18c9eb125dc02b247f6b272c723d5af5cd9fcd Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:11:48 -0500 Subject: [PATCH 116/118] fmt role markdown --- website/docs/r/iam_role.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/iam_role.html.markdown b/website/docs/r/iam_role.html.markdown index 89ac139116a..04e4ce8c8be 100644 --- a/website/docs/r/iam_role.html.markdown +++ b/website/docs/r/iam_role.html.markdown @@ -85,7 +85,7 @@ resource "aws_iam_role" "example" { }, ] }) - "policy-8675309" = data.aws_iam_policy_document.inline_policy.json + "policy-8675309" = data.aws_iam_policy_document.inline_policy.json } } From a4bf59b4613ca343910722541d68cf3149fdeb04 Mon Sep 17 00:00:00 2001 From: teddylear <20077627+teddylear@users.noreply.github.com> Date: Mon, 11 Mar 2024 20:54:57 -0400 Subject: [PATCH 117/118] chore: fix semgrep error with test error check --- internal/service/iam/role_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/iam/role_test.go b/internal/service/iam/role_test.go index d624154a064..8cb38621614 100644 --- a/internal/service/iam/role_test.go +++ b/internal/service/iam/role_test.go @@ -63,7 +63,7 @@ func TestAccIAMRole_MigrateFromPluginSDK_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), CheckDestroy: testAccCheckRoleDestroy(ctx), Steps: []resource.TestStep{ { @@ -742,7 +742,7 @@ func TestAccIAMRole_MigrateFromPluginSDK_InlinePolicy(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), CheckDestroy: testAccCheckRoleDestroy(ctx), Steps: []resource.TestStep{ { @@ -782,7 +782,7 @@ func TestAccIAMRole_InlinePolicy_badJSON(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckRoleDestroy(ctx), Steps: []resource.TestStep{ @@ -912,7 +912,7 @@ func TestAccIAMRole_ManagedPolicy_badARN(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckRoleDestroy(ctx), Steps: []resource.TestStep{ From 9277bf292cfdac0049a7d19b337af8a0e7860fa5 Mon Sep 17 00:00:00 2001 From: "20077627+teddylear@users.noreply.github.com" Date: Wed, 10 Apr 2024 17:53:05 -0400 Subject: [PATCH 118/118] remove autogen tags --- internal/service/iam/role.go | 1 - internal/service/iam/role_tags_gen_test.go | 693 --------------------- 2 files changed, 694 deletions(-) delete mode 100644 internal/service/iam/role_tags_gen_test.go diff --git a/internal/service/iam/role.go b/internal/service/iam/role.go index 7389bdd1e57..d0246f8d7f6 100644 --- a/internal/service/iam/role.go +++ b/internal/service/iam/role.go @@ -58,7 +58,6 @@ const ( // @FrameworkResource(name="Role") // @Tags(identifierAttribute="id", resourceType="Role") -// @Testing(existsType="github.com/aws/aws-sdk-go/service/iam.Role") func NewResourceRole(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourceIamRole{} r.SetMigratedFromPluginSDK(true) diff --git a/internal/service/iam/role_tags_gen_test.go b/internal/service/iam/role_tags_gen_test.go deleted file mode 100644 index 54224f5444b..00000000000 --- a/internal/service/iam/role_tags_gen_test.go +++ /dev/null @@ -1,693 +0,0 @@ -// Code generated by internal/generate/tagstests/main.go; DO NOT EDIT. - -package iam_test - -import ( - "testing" - - "github.com/aws/aws-sdk-go/service/iam" - sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-provider-aws/internal/acctest" - "github.com/hashicorp/terraform-provider-aws/names" -) - -func TestAccIAMRole_tags(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_tags1(rName, "key1", "value1"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccRoleConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), - resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccRoleConfig_tags1(rName, "key2", "value2"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccRoleConfig_tags0(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_null(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_tagsNull(rName, "key1"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccRoleConfig_tags0(rName), - PlanOnly: true, - ExpectNonEmptyPlan: false, - }, - }, - }) -} - -func TestAccIAMRole_tags_AddOnUpdate(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_tags0(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - ), - }, - { - Config: testAccRoleConfig_tags1(rName, "key1", "value1"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_EmptyTag_OnCreate(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_tags1(rName, "key1", ""), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccRoleConfig_tags0(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_EmptyTag_OnUpdate_Add(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_tags1(rName, "key1", "value1"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - ), - }, - { - Config: testAccRoleConfig_tags2(rName, "key1", "value1", "key2", ""), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - resource.TestCheckResourceAttr(resourceName, "tags.key2", ""), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccRoleConfig_tags1(rName, "key1", "value1"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_EmptyTag_OnUpdate_Replace(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_tags1(rName, "key1", "value1"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - ), - }, - { - Config: testAccRoleConfig_tags1(rName, "key1", ""), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_DefaultTags_providerOnly(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("key1", "value1"), - testAccRoleConfig_tags0(rName), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags2("key1", "value1updated", "key2", "value2"), - testAccRoleConfig_tags0(rName), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1updated"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("key2", "value2"), - testAccRoleConfig_tags0(rName), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags0(), - testAccRoleConfig_tags0(rName), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "0"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_DefaultTags_nonOverlapping(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"), - testAccRoleConfig_tags1(rName, "resourcekey1", "resourcevalue1"), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.resourcekey1", "resourcevalue1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey1", "resourcevalue1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1updated"), - testAccRoleConfig_tags2(rName, "resourcekey1", "resourcevalue1updated", "resourcekey2", "resourcevalue2"), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.resourcekey1", "resourcevalue1updated"), - resource.TestCheckResourceAttr(resourceName, "tags.resourcekey2", "resourcevalue2"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "3"), - resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1updated"), - resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey1", "resourcevalue1updated"), - resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey2", "resourcevalue2"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags0(), - testAccRoleConfig_tags0(rName), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "0"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_DefaultTags_overlapping(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("overlapkey1", "providervalue1"), - testAccRoleConfig_tags1(rName, "overlapkey1", "resourcevalue1"), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags2("overlapkey1", "providervalue1", "overlapkey2", "providervalue2"), - testAccRoleConfig_tags2(rName, "overlapkey1", "resourcevalue1", "overlapkey2", "resourcevalue2"), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue1"), - resource.TestCheckResourceAttr(resourceName, "tags.overlapkey2", "resourcevalue2"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey2", "resourcevalue2"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("overlapkey1", "providervalue1"), - testAccRoleConfig_tags1(rName, "overlapkey1", "resourcevalue2"), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue2"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue2"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_DefaultTags_updateToProviderOnly(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccRoleConfig_tags1(rName, "key1", "value1"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), - ), - }, - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("key1", "value1"), - testAccRoleConfig_tags0(rName), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_DefaultTags_updateToResourceOnly(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("key1", "value1"), - testAccRoleConfig_tags0(rName), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), - ), - }, - { - Config: testAccRoleConfig_tags1(rName, "key1", "value1"), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_DefaultTags_emptyResourceTag(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("key1", "value1"), - testAccRoleConfig_tags1(rName, "key1", ""), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key1", ""), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_DefaultTags_nullOverlappingResourceTag(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("key1", "providervalue1"), - testAccRoleConfig_tagsNull(rName, "key1"), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "providervalue1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccIAMRole_tags_DefaultTags_nullNonOverlappingResourceTag(t *testing.T) { - ctx := acctest.Context(t) - var v iam.Role - resourceName := "aws_iam_role.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: testAccCheckRoleDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: acctest.ConfigCompose( - acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"), - testAccRoleConfig_tagsNull(rName, "resourcekey1"), - ), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckRoleExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -}