diff --git a/.changelog/44114.txt b/.changelog/44114.txt new file mode 100644 index 000000000000..98bf5873f3b2 --- /dev/null +++ b/.changelog/44114.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_ram_permission +``` \ No newline at end of file diff --git a/internal/service/ram/exports_test.go b/internal/service/ram/exports_test.go index 564d666be9c0..a1eba99b0685 100644 --- a/internal/service/ram/exports_test.go +++ b/internal/service/ram/exports_test.go @@ -10,10 +10,12 @@ var ( ResourceResourceShare = resourceResourceShare ResourceResourceShareAccepter = resourceResourceShareAccepter ResourceSharingWithOrganization = resourceSharingWithOrganization + ResourcePermission = newPermissionResource FindPrincipalAssociationByTwoPartKey = findPrincipalAssociationByTwoPartKey FindResourceAssociationByTwoPartKey = findResourceAssociationByTwoPartKey FindResourceShareOwnerOtherAccountsByARN = findResourceShareOwnerOtherAccountsByARN FindResourceShareOwnerSelfByARN = findResourceShareOwnerSelfByARN FindSharingWithOrganization = findSharingWithOrganization + FindPermissionByARN = findPermissionByARN ) diff --git a/internal/service/ram/permission.go b/internal/service/ram/permission.go new file mode 100644 index 000000000000..e99bff306df9 --- /dev/null +++ b/internal/service/ram/permission.go @@ -0,0 +1,424 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ram + +import ( + "context" + "errors" + "fmt" + "reflect" + "strconv" + "time" + + "github.com/YakDriver/regexache" + "github.com/YakDriver/smarterr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ram" + awstypes "github.com/aws/aws-sdk-go-v2/service/ram/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + "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/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" + sdkid "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + "github.com/hashicorp/terraform-provider-aws/internal/smerr" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource("aws_ram_permission", name="Permission") +// @ArnIdentity(identityDuplicateAttributes="id") +// @Tags(identifierAttribute="arn") +// @Testing(importStateIdAttribute="arn") +// @Testing(importIgnore="policy_template") +func newPermissionResource(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &resourcePermission{} + + r.SetDefaultDeleteTimeout(10 * time.Minute) + + return r, nil +} + +type resourcePermission struct { + framework.ResourceWithModel[resourcePermissionModel] + framework.WithTimeouts + framework.WithImportByIdentity +} + +func (r *resourcePermission) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + names.AttrARN: framework.ARNAttributeComputedOnly(), + names.AttrCreationTime: schema.StringAttribute{ + CustomType: timetypes.RFC3339Type{}, + Computed: true, + }, + "default_version": schema.BoolAttribute{ + Computed: true, + }, + names.AttrID: framework.IDAttributeDeprecatedWithAlternate(path.Root(names.AttrARN)), + names.AttrLastUpdatedTime: schema.StringAttribute{ + CustomType: timetypes.RFC3339Type{}, + Computed: true, + }, + names.AttrName: schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 36), + stringvalidator.RegexMatches( + regexache.MustCompile(`^\S[\w.-]*$`), + "value must contain letters and numbers", + ), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "policy_template": schema.StringAttribute{ + Required: true, + }, + names.AttrResourceType: schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + names.AttrStatus: schema.StringAttribute{ + Computed: true, + }, + names.AttrTags: tftags.TagsAttribute(), + names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), + names.AttrVersion: schema.StringAttribute{ + Computed: true, + }, + }, + Blocks: map[string]schema.Block{ + names.AttrTimeouts: timeouts.Block(ctx, timeouts.Opts{ + Delete: true, + }), + }, + } +} + +func (r *resourcePermission) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().RAMClient(ctx) + + var plan resourcePermissionModel + smerr.EnrichAppend(ctx, &resp.Diagnostics, req.Plan.Get(ctx, &plan)) + if resp.Diagnostics.HasError() { + return + } + + input := ram.CreatePermissionInput{ + ClientToken: aws.String(sdkid.UniqueId()), + } + smerr.EnrichAppend(ctx, &resp.Diagnostics, flex.Expand(ctx, plan, &input)) + if resp.Diagnostics.HasError() { + return + } + + input.Tags = getTagsIn(ctx) + + out, err := conn.CreatePermission(ctx, &input) + if err != nil { + smerr.AddError(ctx, &resp.Diagnostics, err, smerr.ID, plan.Name.String()) + return + } + if out == nil || out.Permission == nil { + smerr.AddError(ctx, &resp.Diagnostics, errors.New("empty output"), smerr.ID, plan.Name.String()) + return + } + + smerr.EnrichAppend(ctx, &resp.Diagnostics, flex.Flatten(ctx, out.Permission, &plan)) + if resp.Diagnostics.HasError() { + return + } + plan.setID() + smerr.EnrichAppend(ctx, &resp.Diagnostics, resp.State.Set(ctx, plan)) +} + +func (r *resourcePermission) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().RAMClient(ctx) + + var state resourcePermissionModel + smerr.EnrichAppend(ctx, &resp.Diagnostics, req.State.Get(ctx, &state)) + if resp.Diagnostics.HasError() { + return + } + + out, err := findPermissionByARN(ctx, conn, state.ID.ValueString()) + + if tfresource.NotFound(err) { + resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + resp.State.RemoveResource(ctx) + return + } + if err != nil { + smerr.AddError(ctx, &resp.Diagnostics, err, smerr.ID, state.ARN.String()) + return + } + setTagsOut(ctx, out.Tags) + + smerr.EnrichAppend(ctx, &resp.Diagnostics, flex.Flatten(ctx, out, &state)) + if resp.Diagnostics.HasError() { + return + } + + smerr.EnrichAppend(ctx, &resp.Diagnostics, resp.State.Set(ctx, &state)) +} + +func (r *resourcePermission) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + conn := r.Meta().RAMClient(ctx) + + var plan, state resourcePermissionModel + smerr.EnrichAppend(ctx, &resp.Diagnostics, req.Plan.Get(ctx, &plan)) + smerr.EnrichAppend(ctx, &resp.Diagnostics, req.State.Get(ctx, &state)) + if resp.Diagnostics.HasError() { + return + } + + diff, d := flex.Diff(ctx, plan, state) + smerr.EnrichAppend(ctx, &resp.Diagnostics, d) + if resp.Diagnostics.HasError() { + return + } + + if diff.HasChanges() { + if err := permissionPruneVersions(ctx, conn, plan.ARN.ValueString()); err != nil { + smerr.AddError(ctx, &resp.Diagnostics, err, smerr.ID, state.ARN.String()) + return + } + + input := ram.CreatePermissionVersionInput{ + ClientToken: aws.String(sdkid.UniqueId()), + PermissionArn: plan.ARN.ValueStringPointer(), + PolicyTemplate: plan.PolicyTemplate.ValueStringPointer(), + } + + smerr.EnrichAppend(ctx, &resp.Diagnostics, flex.Expand(ctx, plan, &input)) + if resp.Diagnostics.HasError() { + return + } + + out, err := conn.CreatePermissionVersion(ctx, &input) + if err != nil { + smerr.AddError(ctx, &resp.Diagnostics, err, smerr.ID, plan.ARN.String()) + return + } + if out == nil || out.Permission == nil { + smerr.AddError(ctx, &resp.Diagnostics, errors.New("empty output"), smerr.ID, plan.ARN.String()) + return + } + + smerr.EnrichAppend(ctx, &resp.Diagnostics, flex.Flatten(ctx, out.Permission, &plan)) + if resp.Diagnostics.HasError() { + return + } + } + + smerr.EnrichAppend(ctx, &resp.Diagnostics, resp.State.Set(ctx, &plan)) +} + +func (r *resourcePermission) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().RAMClient(ctx) + + var state resourcePermissionModel + smerr.EnrichAppend(ctx, &resp.Diagnostics, req.State.Get(ctx, &state)) + if resp.Diagnostics.HasError() { + return + } + + input := ram.DeletePermissionInput{ + PermissionArn: state.ARN.ValueStringPointer(), + } + + _, err := conn.DeletePermission(ctx, &input) + if err != nil { + if errs.IsA[*awstypes.UnknownResourceException](err) { + return + } + + smerr.AddError(ctx, &resp.Diagnostics, err, smerr.ID, state.ARN.String()) + return + } + + // TIP: -- 5. Use a waiter to wait for delete to complete + deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) + _, err = waitPermissionDeleted(ctx, conn, state.ARN.ValueString(), deleteTimeout) + if err != nil { + smerr.AddError(ctx, &resp.Diagnostics, err, smerr.ID, state.ARN.String()) + return + } +} + +func waitPermissionDeleted(ctx context.Context, conn *ram.Client, arn string, timeout time.Duration) (*awstypes.ResourceSharePermissionDetail, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.PermissionStatusDeleting), + Target: enum.Slice(awstypes.PermissionStatusDeleted), + Refresh: statusPermission(ctx, conn, arn), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.ResourceSharePermissionDetail); ok { + return output, err + } + + return nil, err +} + +func statusPermission(ctx context.Context, conn *ram.Client, arn string) retry.StateRefreshFunc { + return func() (any, string, error) { + output, err := findPermissionByARN(ctx, conn, arn) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.Status), nil + } +} + +func findPermissionByARN(ctx context.Context, conn *ram.Client, arn string) (*awstypes.ResourceSharePermissionDetail, error) { + input := ram.GetPermissionInput{ + PermissionArn: &arn, + } + + out, err := conn.GetPermission(ctx, &input) + if err != nil { + if errs.IsA[*awstypes.UnknownResourceException](err) { + return nil, smarterr.NewError(&retry.NotFoundError{ + LastError: err, + LastRequest: &input, + }) + } + + return nil, smarterr.NewError(err) + } + + if out == nil || out.Permission == nil { + return nil, smarterr.NewError(tfresource.NewEmptyResultError(&input)) + } + + return out.Permission, nil +} + +type resourcePermissionModel struct { + framework.WithRegionModel + ARN types.String `tfsdk:"arn"` + CreationTime timetypes.RFC3339 `tfsdk:"creation_time"` + ID types.String `tfsdk:"id"` + DefaultVersion types.Bool `tfsdk:"default_version"` + LastUpdatedTime timetypes.RFC3339 `tfsdk:"last_updated_time"` + Name types.String `tfsdk:"name"` + PolicyTemplate types.String `tfsdk:"policy_template"` + ResourceType types.String `tfsdk:"resource_type"` + Tags tftags.Map `tfsdk:"tags"` + TagsAll tftags.Map `tfsdk:"tags_all"` + Timeouts timeouts.Value `tfsdk:"timeouts"` + Status types.String `tfsdk:"status"` + Version types.String `tfsdk:"version"` +} + +func (data *resourcePermissionModel) setID() { + data.ID = data.ARN +} + +// policyPruneVersions deletes the oldest version. +// +// Old versions are deleted until there are 4 or less remaining, which means at +// least one more can be created before hitting the maximum of 5. +// +// The default version is never deleted. +func permissionPruneVersions(ctx context.Context, conn *ram.Client, arn string) error { + versions, err := findPermissionVersionsByARN(ctx, conn, arn) + + if err != nil { + return err + } + + if len(versions) < 5 { + return nil + } + + oldestVersion := versions[0] + for _, version := range versions { + if *version.DefaultVersion { + continue + } + + if version.CreationTime.Before(aws.ToTime(oldestVersion.CreationTime)) { + oldestVersion = version + } + } + + versionInt, err := strconv.Atoi(aws.ToString(oldestVersion.Version)) + if err != nil { + return fmt.Errorf("failed to parse version '%s' to int: %w", aws.ToString(oldestVersion.Version), err) + } + return permissionDeleteVersion(ctx, conn, arn, int32(versionInt)) +} + +func permissionDeleteVersion(ctx context.Context, conn *ram.Client, arn string, versionID int32) error { + input := &ram.DeletePermissionVersionInput{ + PermissionArn: aws.String(arn), + PermissionVersion: aws.Int32(versionID), + } + + _, err := conn.DeletePermissionVersion(ctx, input) + + if err != nil { + return fmt.Errorf("deleting RAM Permission (%s) version (%d): %w", arn, versionID, err) + } + + return nil +} + +func findPermissionVersionsByARN(ctx context.Context, conn *ram.Client, arn string) ([]awstypes.ResourceSharePermissionSummary, error) { + input := &ram.ListPermissionVersionsInput{ + PermissionArn: aws.String(arn), + } + var output []awstypes.ResourceSharePermissionSummary + + pages := ram.NewListPermissionVersionsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*awstypes.UnknownResourceException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + for _, v := range page.Permissions { + if !reflect.ValueOf(v).IsZero() { + output = append(output, v) + } + } + } + + return output, nil +} diff --git a/internal/service/ram/permission_test.go b/internal/service/ram/permission_test.go new file mode 100644 index 000000000000..9f23d57bebb4 --- /dev/null +++ b/internal/service/ram/permission_test.go @@ -0,0 +1,312 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ram_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/ram" + awstypes "github.com/aws/aws-sdk-go-v2/service/ram/types" + 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" + tfram "github.com/hashicorp/terraform-provider-aws/internal/service/ram" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccRAMPermission_basic(t *testing.T) { + ctx := acctest.Context(t) + + var permission awstypes.ResourceSharePermissionDetail + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_ram_permission.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.RAM) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.RAMServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPermissionDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPermissionConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPermissionExists(ctx, resourceName, &permission), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + resource.TestCheckResourceAttrSet(resourceName, names.AttrCreationTime), + resource.TestCheckResourceAttrSet(resourceName, names.AttrLastUpdatedTime), + resource.TestCheckResourceAttrSet(resourceName, names.AttrVersion), + resource.TestCheckResourceAttrSet(resourceName, names.AttrStatus), + resource.TestCheckResourceAttrSet(resourceName, "default_version"), + acctest.CheckResourceAttrRegionalARNFormat(ctx, resourceName, names.AttrARN, "ram", "permission/{name}"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"policy_template"}, + }, + }, + }) +} + +func TestAccRAMPermission_version(t *testing.T) { + ctx := acctest.Context(t) + + var permission awstypes.ResourceSharePermissionDetail + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_ram_permission.test" + policyTemplateActions := []string{ + "backup:ListProtectedResourcesByBackupVault", + "backup:ListRecoveryPointsByBackupVault", + "backup:DescribeRecoveryPoint", + "backup:DescribeBackupVault", + "backup:StartRestoreJob", + } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.RAM) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.RAMServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPermissionDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPermissionConfig_version(rName, `"`+strings.Join(policyTemplateActions[:3], `", "`)+`"`), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPermissionExists(ctx, resourceName, &permission), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + acctest.CheckResourceAttrRegionalARNFormat(ctx, resourceName, names.AttrARN, "ram", "permission/{name}"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"policy_template"}, + }, + { + Config: testAccPermissionConfig_version(rName, `"`+strings.Join(policyTemplateActions[1:2], `", "`)+`"`), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPermissionExists(ctx, resourceName, &permission), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + acctest.CheckResourceAttrRegionalARNFormat(ctx, resourceName, names.AttrARN, "ram", "permission/{name}"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"policy_template"}, + }, + { + Config: testAccPermissionConfig_version(rName, `"`+strings.Join(policyTemplateActions[3:4], `", "`)+`"`), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPermissionExists(ctx, resourceName, &permission), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + acctest.CheckResourceAttrRegionalARNFormat(ctx, resourceName, names.AttrARN, "ram", "permission/{name}"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"policy_template"}, + }, + { + Config: testAccPermissionConfig_version(rName, `"`+strings.Join(policyTemplateActions[:4], `", "`)+`"`), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPermissionExists(ctx, resourceName, &permission), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + acctest.CheckResourceAttrRegionalARNFormat(ctx, resourceName, names.AttrARN, "ram", "permission/{name}"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"policy_template"}, + }, + { + Config: testAccPermissionConfig_version(rName, `"`+strings.Join(policyTemplateActions[2:4], `", "`)+`"`), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPermissionExists(ctx, resourceName, &permission), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + acctest.CheckResourceAttrRegionalARNFormat(ctx, resourceName, names.AttrARN, "ram", "permission/{name}"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"policy_template"}, + }, + { + Config: testAccPermissionConfig_version(rName, `"`+strings.Join(policyTemplateActions, `", "`)+`"`), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPermissionExists(ctx, resourceName, &permission), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + acctest.CheckResourceAttrRegionalARNFormat(ctx, resourceName, names.AttrARN, "ram", "permission/{name}"), + ), + }, + }, + }) +} + +func TestAccRAMPermission_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var permission awstypes.ResourceSharePermissionDetail + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_ram_permission.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.RAM) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.RAMServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPermissionDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPermissionConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPermissionExists(ctx, resourceName, &permission), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfram.ResourcePermission, resourceName), + ), + ExpectNonEmptyPlan: true, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + }, + }, + }, + }, + }) +} + +func testAccCheckPermissionDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).RAMClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_ram_permission" { + continue + } + + output, err := tfram.FindPermissionByARN(ctx, conn, rs.Primary.Attributes[names.AttrARN]) + if tfresource.NotFound(err) { + return nil + } + if err != nil { + return err + } + if output != nil && output.Status == awstypes.PermissionStatusDeleted { + return nil + } + + return fmt.Errorf("RAM Permission %s still exists", rs.Primary.Attributes[names.AttrARN]) + } + + return nil + } +} + +func testAccCheckPermissionExists(ctx context.Context, name string, permission *awstypes.ResourceSharePermissionDetail) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).RAMClient(ctx) + + resp, err := tfram.FindPermissionByARN(ctx, conn, rs.Primary.Attributes[names.AttrARN]) + if err != nil { + return err + } + + *permission = *resp + + return nil + } +} + +func testAccPreCheck(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).RAMClient(ctx) + + input := &ram.ListPermissionsInput{} + + _, err := conn.ListPermissions(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccPermissionConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_ram_permission" "test" { + name = %[1]q + policy_template = <:` and is not case sensitive. +* `tags` - (Optional) A map of tags to assign to the resource share. 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. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Permission. +* `creation_time` - The date and time when the permission was created. +* `default_version` - Specifies whether the version of the managed permission used by this resource share is the default version for this managed permission. +* `last_updated_time` - The date and time when the permission was last updated. +* `status` - The current status of the permission. +* `version` - The version of the permission associated with this resource share. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `delete` - (Default `10m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import RAM (Resource Access Manager) Permission using the `arn`. For example: + +```terraform +import { + to = aws_ram_permission.example + id = "arn:aws:ram:us-west-1:123456789012:permission/test-permission" +} +``` + +Using `terraform import`, import RAM (Resource Access Manager) Permission using the `example_id_arg`. For example: + +```console +% terraform import aws_ram_permission.example arn:aws:ram:us-west-1:123456789012:permission/test-permission +```