From 900a92aeb357fd99b6bc3aa60af4204c5352f8ad Mon Sep 17 00:00:00 2001 From: Daniel Rieske Date: Fri, 8 Dec 2023 16:11:16 +0100 Subject: [PATCH 01/11] feat: added resource tests and docs --- .../ssoadmin/application_access_scope.go | 193 ++++++++++++++++++ .../ssoadmin/application_access_scope_test.go | 153 ++++++++++++++ .../service/ssoadmin/service_package_gen.go | 4 + ...min_application_access_scope.html.markdown | 64 ++++++ 4 files changed, 414 insertions(+) create mode 100644 internal/service/ssoadmin/application_access_scope.go create mode 100644 internal/service/ssoadmin/application_access_scope_test.go create mode 100644 website/docs/r/ssoadmin_application_access_scope.html.markdown diff --git a/internal/service/ssoadmin/application_access_scope.go b/internal/service/ssoadmin/application_access_scope.go new file mode 100644 index 000000000000..228f6a918c35 --- /dev/null +++ b/internal/service/ssoadmin/application_access_scope.go @@ -0,0 +1,193 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ssoadmin + +import ( + "context" + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ssoadmin" + "github.com/aws/aws-sdk-go-v2/service/ssoadmin/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "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/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +// @SDKResource("aws_ssoadmin_application_access_scope") +func ResourceApplicationAccessScope() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceApplicationAccessScopeCreate, + ReadWithoutTimeout: resourceApplicationAccessScopeRead, + DeleteWithoutTimeout: resourceApplicationAccessScopeDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "application_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: verify.ValidARN, + }, + "authorized_targets": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: verify.ValidARN, + }, + }, + "scope": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceApplicationAccessScopeCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).SSOAdminClient(ctx) + + applicationARN := d.Get("application_arn").(string) + scope := d.Get("scope").(string) + id := ApplicationAccessScopeCreateResourceID(applicationARN, scope) + + input := &ssoadmin.PutApplicationAccessScopeInput{ + ApplicationArn: aws.String(applicationARN), + Scope: aws.String(scope), + } + + if v, ok := d.GetOk("authorized_targets"); ok { + input.AuthorizedTargets = flex.ExpandStringValueList(v.([]interface{})) + } + + _, err := conn.PutApplicationAccessScope(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "creating SSO Application Access Scope (%s): %s", id, err) + } + + d.SetId(id) + + return append(diags, resourceApplicationAccessScopeRead(ctx, d, meta)...) +} + +func resourceApplicationAccessScopeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).SSOAdminClient(ctx) + + applicationARN, scope, err := ApplicationAccessScopeParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + output, err := FindApplicationAccessScopeByScopeAndApplicationARN(ctx, conn, applicationARN, scope) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] SSO Application Access Scope (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading SSO Application Access Scope (%s): %s", d.Id(), err) + } + + d.Set("application_arn", applicationARN) + d.Set("scope", output.Scope) + d.Set("authorized_targets", output.AuthorizedTargets) + + return diags +} + +func resourceApplicationAccessScopeDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).SSOAdminClient(ctx) + + applicationARN, scope, err := ApplicationAccessScopeParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + log.Printf("[INFO] Deleting SSO Application Access Scope: %s", d.Id()) + _, err = conn.DeleteApplicationAccessScope(ctx, &ssoadmin.DeleteApplicationAccessScopeInput{ + ApplicationArn: aws.String(applicationARN), + Scope: aws.String(scope), + }) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting SSO Application Access Scope (%s): %s", d.Id(), err) + } + + return diags +} + +const applicationAccessScopeIDSeparator = "," + +func ApplicationAccessScopeCreateResourceID(applicationARN, scope string) string { + parts := []string{applicationARN, scope} + id := strings.Join(parts, applicationAccessScopeIDSeparator) + + return id +} + +func ApplicationAccessScopeParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, applicationAccessScopeIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected APPLICATION_ARN%[2]sSCOPE%[2]s", id, applicationAccessScopeIDSeparator) +} + +func FindApplicationAccessScopeByScopeAndApplicationARN(ctx context.Context, conn *ssoadmin.Client, applicationARN, scope string) (*ssoadmin.GetApplicationAccessScopeOutput, error) { + input := &ssoadmin.GetApplicationAccessScopeInput{ + ApplicationArn: aws.String(applicationARN), + Scope: aws.String(scope), + } + + output, err := conn.GetApplicationAccessScope(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/ssoadmin/application_access_scope_test.go b/internal/service/ssoadmin/application_access_scope_test.go new file mode 100644 index 000000000000..55a294ce5558 --- /dev/null +++ b/internal/service/ssoadmin/application_access_scope_test.go @@ -0,0 +1,153 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ssoadmin_test + +import ( + "context" + "fmt" + "testing" + + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "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/ssoadmin" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccSSOAdminApplicationAccessScope_basic(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_ssoadmin_application_access_scope.test" + applicationResourceName := "aws_ssoadmin_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SSOAdminEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckApplicationAccessScopeDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccApplicationAccessScopeConfig_basic(rName, "sso:account:access"), + Check: resource.ComposeTestCheckFunc( + testAccCheckApplicationAccessScopeExists(ctx, resourceName), + resource.TestCheckResourceAttrPair(resourceName, "application_arn", applicationResourceName, "application_arn"), + resource.TestCheckResourceAttr(resourceName, "scope", "sso:account:access"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccApplicationAccessScopeImportStateIdFunc(resourceName), + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccSSOAdminApplicationAccessScope_disappears(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_ssoadmin_application_access_scope.test" + applicationResourceName := "aws_ssoadmin_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SSOAdminEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckApplicationAccessScopeDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccApplicationAccessScopeConfig_basic(rName, "sso:account:access"), + Check: resource.ComposeTestCheckFunc( + testAccCheckApplicationAccessScopeExists(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, ssoadmin.ResourceApplicationAccessScope(), applicationResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckApplicationAccessScopeExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).SSOAdminClient(ctx) + + applicationARN, scope, err := ssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) + if err != nil { + return err + } + + _, err = ssoadmin.FindApplicationAccessScopeByScopeAndApplicationARN(ctx, conn, applicationARN, scope) + + return err + } +} + +func testAccCheckApplicationAccessScopeDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).SSOAdminClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_ssoadmin_application_access_scope" { + continue + } + + var applicationARN, scope, err = ssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) + if err != nil { + return err + } + + _, err = ssoadmin.FindApplicationAccessScopeByScopeAndApplicationARN(ctx, conn, applicationARN, scope) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("SSO Application Access Scope %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccApplicationAccessScopeImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not Found: %s", resourceName) + } + + return fmt.Sprintf("%s,%s", rs.Primary.Attributes["application_arn"], rs.Primary.Attributes["scope"]), nil + } +} + +func testAccApplicationAccessScopeConfig_basic(rName, scope string) string { + return fmt.Sprintf(` +data "aws_ssoadmin_instances" "test" {} + +resource "aws_ssoadmin_application" "test" { + name = %[1]q + application_provider_arn = %[2]q + instance_arn = tolist(data.aws_ssoadmin_instances.test.arns)[0] +} + +resource "aws_ssoadmin_application_access_scope" "test" { + application_arn = aws_ssoadmin_application.test.application_arn + authorized_targets = [aws_ssoadmin_application.test.application_arn] + scope = %[3]q +} +`, rName, testAccApplicationProviderARN, scope) +} diff --git a/internal/service/ssoadmin/service_package_gen.go b/internal/service/ssoadmin/service_package_gen.go index 882e10d2b485..839d43a4be28 100644 --- a/internal/service/ssoadmin/service_package_gen.go +++ b/internal/service/ssoadmin/service_package_gen.go @@ -62,6 +62,10 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka Factory: ResourceAccountAssignment, TypeName: "aws_ssoadmin_account_assignment", }, + { + Factory: ResourceApplicationAccessScope, + TypeName: "aws_ssoadmin_application_access_scope", + }, { Factory: ResourceCustomerManagedPolicyAttachment, TypeName: "aws_ssoadmin_customer_managed_policy_attachment", diff --git a/website/docs/r/ssoadmin_application_access_scope.html.markdown b/website/docs/r/ssoadmin_application_access_scope.html.markdown new file mode 100644 index 000000000000..c65ea868bc8a --- /dev/null +++ b/website/docs/r/ssoadmin_application_access_scope.html.markdown @@ -0,0 +1,64 @@ +--- +subcategory: "SSO Admin" +layout: "aws" +page_title: "AWS: aws_ssoadmin_application_access_scope" +description: |- + Terraform resource for managing an AWS SSO Admin Application Access Scope. +--- +# Resource: aws_ssoadmin_application_access_scope + +Terraform resource for managing an AWS SSO Admin Application Access Scope. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_ssoadmin_instances" "example" {} + +resource "aws_ssoadmin_application" "example" { + name = "example" + application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom" + instance_arn = tolist(data.aws_ssoadmin_instances.example.arns)[0] +} + +resource "aws_ssoadmin_application_access_scope" "test" { + application_arn = aws_ssoadmin_application.test.application_arn + authorized_targets = [ "arn:aws:sso::012345678901:application/ssoins-012345678901/apl-012345678901" ] + scope = "sso:account:access" +} +``` + +## Argument Reference + +The following arguments are required: + +* `application_arn` - (Required) Specifies the ARN of the application with the access scope with the targets to add or update. +* `scope` - (Required) Specifies the name of the access scope to be associated with the specified targets. + +The following arguments are optional: + +* `authorized_targets` - (Optional) Specifies an array list of ARNs that represent the authorized targets for this access scope. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `id` - ARN of the application. + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import SSO Admin Application Access Scope using the `id`. For example: + +```terraform +import { + to = aws_ssoadmin_application_access_scope.example + id = "arn:aws:sso::012345678901:application/ssoins-012345678901/apl-012345678901,sso:account:access" +} +``` + +Using `terraform import`, import SSO Admin Application Access Scope using the `id`. For example: + +```console +% terraform import aws_ssoadmin_application_access_scope.example arn:aws:sso::012345678901:application/ssoins-012345678901/apl-012345678901,sso:account:access +``` From 6d41c27d409f6790ae0bc70b0117347742da032b Mon Sep 17 00:00:00 2001 From: Daniel Rieske Date: Fri, 8 Dec 2023 16:24:59 +0100 Subject: [PATCH 02/11] feat: fix dissapear test --- internal/service/ssoadmin/application_access_scope.go | 2 +- internal/service/ssoadmin/application_access_scope_test.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/service/ssoadmin/application_access_scope.go b/internal/service/ssoadmin/application_access_scope.go index 228f6a918c35..325a6c9d7709 100644 --- a/internal/service/ssoadmin/application_access_scope.go +++ b/internal/service/ssoadmin/application_access_scope.go @@ -163,7 +163,7 @@ func ApplicationAccessScopeParseResourceID(id string) (string, string, error) { return parts[0], parts[1], nil } - return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected APPLICATION_ARN%[2]sSCOPE%[2]s", id, applicationAccessScopeIDSeparator) + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected APPLICATION_ARN%[2]sSCOPE", id, applicationAccessScopeIDSeparator) } func FindApplicationAccessScopeByScopeAndApplicationARN(ctx context.Context, conn *ssoadmin.Client, applicationARN, scope string) (*ssoadmin.GetApplicationAccessScopeOutput, error) { diff --git a/internal/service/ssoadmin/application_access_scope_test.go b/internal/service/ssoadmin/application_access_scope_test.go index 55a294ce5558..534d378ef7fc 100644 --- a/internal/service/ssoadmin/application_access_scope_test.go +++ b/internal/service/ssoadmin/application_access_scope_test.go @@ -52,7 +52,6 @@ func TestAccSSOAdminApplicationAccessScope_disappears(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ssoadmin_application_access_scope.test" - applicationResourceName := "aws_ssoadmin_application.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -64,7 +63,7 @@ func TestAccSSOAdminApplicationAccessScope_disappears(t *testing.T) { Config: testAccApplicationAccessScopeConfig_basic(rName, "sso:account:access"), Check: resource.ComposeTestCheckFunc( testAccCheckApplicationAccessScopeExists(ctx, resourceName), - acctest.CheckResourceDisappears(ctx, acctest.Provider, ssoadmin.ResourceApplicationAccessScope(), applicationResourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, ssoadmin.ResourceApplicationAccessScope(), resourceName), ), ExpectNonEmptyPlan: true, }, From 6956422bfad4ce76eadf3d1dc3248c43bb365df0 Mon Sep 17 00:00:00 2001 From: Daniel Rieske Date: Fri, 8 Dec 2023 16:26:37 +0100 Subject: [PATCH 03/11] chore: added changelog --- .changelog/34811.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/34811.txt diff --git a/.changelog/34811.txt b/.changelog/34811.txt new file mode 100644 index 000000000000..9089df669d9f --- /dev/null +++ b/.changelog/34811.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_ssoadmin_application_access_scope +``` \ No newline at end of file From 914d70eb71423a2cadc9622ca474315e3fcb0bee Mon Sep 17 00:00:00 2001 From: Daniel Rieske Date: Fri, 8 Dec 2023 16:29:13 +0100 Subject: [PATCH 04/11] chore: fmt examples --- website/docs/r/ssoadmin_application_access_scope.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/ssoadmin_application_access_scope.html.markdown b/website/docs/r/ssoadmin_application_access_scope.html.markdown index c65ea868bc8a..d00481b9a0c7 100644 --- a/website/docs/r/ssoadmin_application_access_scope.html.markdown +++ b/website/docs/r/ssoadmin_application_access_scope.html.markdown @@ -24,7 +24,7 @@ resource "aws_ssoadmin_application" "example" { resource "aws_ssoadmin_application_access_scope" "test" { application_arn = aws_ssoadmin_application.test.application_arn - authorized_targets = [ "arn:aws:sso::012345678901:application/ssoins-012345678901/apl-012345678901" ] + authorized_targets = ["arn:aws:sso::012345678901:application/ssoins-012345678901/apl-012345678901"] scope = "sso:account:access" } ``` From bd356b874e997b8e9e628ca2dfb417094efa1d63 Mon Sep 17 00:00:00 2001 From: Daniel Rieske Date: Fri, 8 Dec 2023 17:55:02 +0100 Subject: [PATCH 05/11] chore: correted changelog --- .changelog/34811.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/34811.txt b/.changelog/34811.txt index 9089df669d9f..4e8547747df7 100644 --- a/.changelog/34811.txt +++ b/.changelog/34811.txt @@ -1,3 +1,3 @@ -```release-note:new-data-source +```release-note:new-resource aws_ssoadmin_application_access_scope ``` \ No newline at end of file From a021529946157c30e2072b4d1cad659323f20f55 Mon Sep 17 00:00:00 2001 From: Daniel Rieske Date: Fri, 8 Dec 2023 19:29:36 +0100 Subject: [PATCH 06/11] fmt: fixed docs --- .../docs/r/ssoadmin_application_access_scope.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/ssoadmin_application_access_scope.html.markdown b/website/docs/r/ssoadmin_application_access_scope.html.markdown index d00481b9a0c7..cf263e39d8f3 100644 --- a/website/docs/r/ssoadmin_application_access_scope.html.markdown +++ b/website/docs/r/ssoadmin_application_access_scope.html.markdown @@ -22,8 +22,8 @@ resource "aws_ssoadmin_application" "example" { instance_arn = tolist(data.aws_ssoadmin_instances.example.arns)[0] } -resource "aws_ssoadmin_application_access_scope" "test" { - application_arn = aws_ssoadmin_application.test.application_arn +resource "aws_ssoadmin_application_access_scope" "example" { + application_arn = aws_ssoadmin_application.example.application_arn authorized_targets = ["arn:aws:sso::012345678901:application/ssoins-012345678901/apl-012345678901"] scope = "sso:account:access" } From 5cafc44c4579e3642c00ba3682fa8e1f0c1e05f1 Mon Sep 17 00:00:00 2001 From: Daniel Rieske Date: Tue, 12 Dec 2023 17:46:14 +0100 Subject: [PATCH 07/11] chore: migrate to plugin-framework --- .../ssoadmin/application_access_scope.go | 239 ++++++++++-------- .../ssoadmin/application_access_scope_test.go | 66 ++--- internal/service/ssoadmin/exports_test.go | 2 + .../service/ssoadmin/service_package_gen.go | 8 +- 4 files changed, 178 insertions(+), 137 deletions(-) diff --git a/internal/service/ssoadmin/application_access_scope.go b/internal/service/ssoadmin/application_access_scope.go index 325a6c9d7709..7e8dc6e1951d 100644 --- a/internal/service/ssoadmin/application_access_scope.go +++ b/internal/service/ssoadmin/application_access_scope.go @@ -5,155 +5,188 @@ package ssoadmin import ( "context" + "errors" "fmt" - "log" "strings" - "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ssoadmin" - "github.com/aws/aws-sdk-go-v2/service/ssoadmin/types" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + awstypes "github.com/aws/aws-sdk-go-v2/service/ssoadmin/types" + "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/listplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/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" + "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/tfresource" - "github.com/hashicorp/terraform-provider-aws/internal/verify" + "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKResource("aws_ssoadmin_application_access_scope") -func ResourceApplicationAccessScope() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceApplicationAccessScopeCreate, - ReadWithoutTimeout: resourceApplicationAccessScopeRead, - DeleteWithoutTimeout: resourceApplicationAccessScopeDelete, +// @FrameworkResource(name="Application Access Scope") +func newResourceApplicationAccessScope(_ context.Context) (resource.ResourceWithConfigure, error) { + return &resourceApplicationAccessScope{}, nil +} - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, +const ( + ResNameApplicationAccessScope = "Application Access Scope" +) - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(10 * time.Minute), - Delete: schema.DefaultTimeout(10 * time.Minute), - }, +type resourceApplicationAccessScope struct { + framework.ResourceWithConfigure +} + +func (r *resourceApplicationAccessScope) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_ssoadmin_application_access_scope" +} - Schema: map[string]*schema.Schema{ - "application_arn": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: verify.ValidARN, +func (r *resourceApplicationAccessScope) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "application_arn": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, - "authorized_targets": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: verify.ValidARN, + "authorized_targets": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), }, }, - "scope": { - Type: schema.TypeString, + "id": framework.IDAttribute(), + "scope": schema.StringAttribute{ Required: true, - ForceNew: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, } } -func resourceApplicationAccessScopeCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).SSOAdminClient(ctx) +func (r *resourceApplicationAccessScope) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().SSOAdminClient(ctx) - applicationARN := d.Get("application_arn").(string) - scope := d.Get("scope").(string) - id := ApplicationAccessScopeCreateResourceID(applicationARN, scope) - - input := &ssoadmin.PutApplicationAccessScopeInput{ - ApplicationArn: aws.String(applicationARN), - Scope: aws.String(scope), + var plan resourceApplicationAccessScopeData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return } - if v, ok := d.GetOk("authorized_targets"); ok { - input.AuthorizedTargets = flex.ExpandStringValueList(v.([]interface{})) + in := &ssoadmin.PutApplicationAccessScopeInput{ + ApplicationArn: aws.String(plan.ApplicationARN.ValueString()), + Scope: aws.String(plan.Scope.ValueString()), } - _, err := conn.PutApplicationAccessScope(ctx, input) + if !plan.AuthorizedTargets.IsNull() { + in.AuthorizedTargets = flex.ExpandFrameworkStringValueList(ctx, plan.AuthorizedTargets) + } + out, err := conn.PutApplicationAccessScope(ctx, in) if err != nil { - return sdkdiag.AppendErrorf(diags, "creating SSO Application Access Scope (%s): %s", id, err) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.SSOAdmin, create.ErrActionCreating, ResNameApplicationAccessScope, plan.ApplicationARN.String(), err), + err.Error(), + ) + return + } + if out == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.SSOAdmin, create.ErrActionCreating, ResNameApplicationAccessScope, plan.ApplicationARN.String(), nil), + errors.New("empty output").Error(), + ) + return } - d.SetId(id) + plan.ID = flex.StringToFramework(ctx, ApplicationAccessScopeCreateResourceID(plan.ApplicationARN.ValueString(), plan.Scope.ValueString())) - return append(diags, resourceApplicationAccessScopeRead(ctx, d, meta)...) + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) } -func resourceApplicationAccessScopeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).SSOAdminClient(ctx) +func (r *resourceApplicationAccessScope) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().SSOAdminClient(ctx) - applicationARN, scope, err := ApplicationAccessScopeParseResourceID(d.Id()) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) + var state resourceApplicationAccessScopeData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return } - output, err := FindApplicationAccessScopeByScopeAndApplicationARN(ctx, conn, applicationARN, scope) + applicationARN, scope, err := ApplicationAccessScopeParseResourceID(state.ID.ValueString()) - if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] SSO Application Access Scope (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags + out, err := findApplicationAccessScopeByID(ctx, conn, applicationARN, scope) + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return } - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading SSO Application Access Scope (%s): %s", d.Id(), err) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.SSOAdmin, create.ErrActionSetting, ResNameApplicationAccessScope, state.ID.String(), err), + err.Error(), + ) + return } - d.Set("application_arn", applicationARN) - d.Set("scope", output.Scope) - d.Set("authorized_targets", output.AuthorizedTargets) + state.ApplicationARN = flex.StringToFrameworkARN(ctx, aws.String(applicationARN)) + state.AuthorizedTargets = flex.FlattenFrameworkStringValueList(ctx, out.AuthorizedTargets) + state.Scope = flex.StringToFramework(ctx, out.Scope) - return diags + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func resourceApplicationAccessScopeDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).SSOAdminClient(ctx) +func (r *resourceApplicationAccessScope) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + //Update is no-op. +} - applicationARN, scope, err := ApplicationAccessScopeParseResourceID(d.Id()) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) +func (r *resourceApplicationAccessScope) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().SSOAdminClient(ctx) + + var state resourceApplicationAccessScopeData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return } - log.Printf("[INFO] Deleting SSO Application Access Scope: %s", d.Id()) - _, err = conn.DeleteApplicationAccessScope(ctx, &ssoadmin.DeleteApplicationAccessScopeInput{ + applicationARN, scope, err := ApplicationAccessScopeParseResourceID(state.ID.ValueString()) + in := &ssoadmin.DeleteApplicationAccessScopeInput{ ApplicationArn: aws.String(applicationARN), Scope: aws.String(scope), - }) - - if errs.IsA[*types.ResourceNotFoundException](err) { - return diags } + _, err = conn.DeleteApplicationAccessScope(ctx, in) if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting SSO Application Access Scope (%s): %s", d.Id(), err) + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.SSOAdmin, create.ErrActionDeleting, ResNameApplicationAccessScope, state.ID.String(), err), + err.Error(), + ) + return } +} - return diags +func (r *resourceApplicationAccessScope) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } const applicationAccessScopeIDSeparator = "," -func ApplicationAccessScopeCreateResourceID(applicationARN, scope string) string { +func ApplicationAccessScopeCreateResourceID(applicationARN, scope string) *string { parts := []string{applicationARN, scope} id := strings.Join(parts, applicationAccessScopeIDSeparator) - return id + return &id } func ApplicationAccessScopeParseResourceID(id string) (string, string, error) { @@ -166,28 +199,34 @@ func ApplicationAccessScopeParseResourceID(id string) (string, string, error) { return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected APPLICATION_ARN%[2]sSCOPE", id, applicationAccessScopeIDSeparator) } -func FindApplicationAccessScopeByScopeAndApplicationARN(ctx context.Context, conn *ssoadmin.Client, applicationARN, scope string) (*ssoadmin.GetApplicationAccessScopeOutput, error) { - input := &ssoadmin.GetApplicationAccessScopeInput{ +func findApplicationAccessScopeByID(ctx context.Context, conn *ssoadmin.Client, applicationARN, scope string) (*ssoadmin.GetApplicationAccessScopeOutput, error) { + in := &ssoadmin.GetApplicationAccessScopeInput{ ApplicationArn: aws.String(applicationARN), Scope: aws.String(scope), } - output, err := conn.GetApplicationAccessScope(ctx, input) - - if errs.IsA[*types.ResourceNotFoundException](err) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, + out, err := conn.GetApplicationAccessScope(ctx, in) + if err != nil { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } } - } - if err != nil { return nil, err } - if output == nil { - return nil, tfresource.NewEmptyResultError(input) + if out == nil { + return nil, tfresource.NewEmptyResultError(in) } - return output, nil + return out, nil +} + +type resourceApplicationAccessScopeData struct { + ApplicationARN fwtypes.ARN `tfsdk:"application_arn"` + AuthorizedTargets types.List `tfsdk:"authorized_targets"` + ID types.String `tfsdk:"id"` + Scope types.String `tfsdk:"scope"` } diff --git a/internal/service/ssoadmin/application_access_scope_test.go b/internal/service/ssoadmin/application_access_scope_test.go index 534d378ef7fc..e49cc40ac31e 100644 --- a/internal/service/ssoadmin/application_access_scope_test.go +++ b/internal/service/ssoadmin/application_access_scope_test.go @@ -5,16 +5,19 @@ package ssoadmin_test import ( "context" + "errors" "fmt" "testing" + "github.com/aws/aws-sdk-go-v2/service/ssoadmin/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "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/ssoadmin" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + tfssoadmin "github.com/hashicorp/terraform-provider-aws/internal/service/ssoadmin" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -63,7 +66,7 @@ func TestAccSSOAdminApplicationAccessScope_disappears(t *testing.T) { Config: testAccApplicationAccessScopeConfig_basic(rName, "sso:account:access"), Check: resource.ComposeTestCheckFunc( testAccCheckApplicationAccessScopeExists(ctx, resourceName), - acctest.CheckResourceDisappears(ctx, acctest.Provider, ssoadmin.ResourceApplicationAccessScope(), resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfssoadmin.ResourceApplicationAccessScope, resourceName), ), ExpectNonEmptyPlan: true, }, @@ -71,26 +74,6 @@ func TestAccSSOAdminApplicationAccessScope_disappears(t *testing.T) { }) } -func testAccCheckApplicationAccessScopeExists(ctx context.Context, n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).SSOAdminClient(ctx) - - applicationARN, scope, err := ssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) - if err != nil { - return err - } - - _, err = ssoadmin.FindApplicationAccessScopeByScopeAndApplicationARN(ctx, conn, applicationARN, scope) - - return err - } -} - func testAccCheckApplicationAccessScopeDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).SSOAdminClient(ctx) @@ -100,22 +83,39 @@ func testAccCheckApplicationAccessScopeDestroy(ctx context.Context) resource.Tes continue } - var applicationARN, scope, err = ssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) + applicationARN, scope, err := tfssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) + _, err = tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, applicationARN, scope) + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil + } if err != nil { - return err + return create.Error(names.SSOAdmin, create.ErrActionCheckingDestroyed, tfssoadmin.ResNameApplicationAccessScope, rs.Primary.ID, err) } - _, err = ssoadmin.FindApplicationAccessScopeByScopeAndApplicationARN(ctx, conn, applicationARN, scope) + return create.Error(names.SSOAdmin, create.ErrActionCheckingDestroyed, tfssoadmin.ResNameApplicationAccessScope, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} - if tfresource.NotFound(err) { - continue - } +func testAccCheckApplicationAccessScopeExists(ctx context.Context, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.SSOAdmin, create.ErrActionCheckingExistence, tfssoadmin.ResNameApplicationAccessScope, name, errors.New("not found")) + } - if err != nil { - return err - } + if rs.Primary.ID == "" { + return create.Error(names.SSOAdmin, create.ErrActionCheckingExistence, tfssoadmin.ResNameApplicationAccessScope, name, errors.New("not set")) + } - return fmt.Errorf("SSO Application Access Scope %s still exists", rs.Primary.ID) + conn := acctest.Provider.Meta().(*conns.AWSClient).SSOAdminClient(ctx) + + applicationARN, scope, err := tfssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) + _, err = tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, applicationARN, scope) + if err != nil { + return create.Error(names.SSOAdmin, create.ErrActionCheckingExistence, tfssoadmin.ResNameApplicationAccessScope, rs.Primary.ID, err) } return nil diff --git a/internal/service/ssoadmin/exports_test.go b/internal/service/ssoadmin/exports_test.go index 4ab0999ae72d..e76751bccd92 100644 --- a/internal/service/ssoadmin/exports_test.go +++ b/internal/service/ssoadmin/exports_test.go @@ -8,8 +8,10 @@ var ( ResourceApplication = newResourceApplication ResourceApplicationAssignment = newResourceApplicationAssignment ResourceApplicationAssignmentConfiguration = newResourceApplicationAssignmentConfiguration + ResourceApplicationAccessScope = newResourceApplicationAccessScope FindApplicationByID = findApplicationByID FindApplicationAssignmentByID = findApplicationAssignmentByID FindApplicationAssignmentConfigurationByID = findApplicationAssignmentConfigurationByID + FindApplicationAccessScopeByID = findApplicationAccessScopeByID ) diff --git a/internal/service/ssoadmin/service_package_gen.go b/internal/service/ssoadmin/service_package_gen.go index 839d43a4be28..78214b260bf0 100644 --- a/internal/service/ssoadmin/service_package_gen.go +++ b/internal/service/ssoadmin/service_package_gen.go @@ -32,6 +32,10 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic Name: "Application", Tags: &types.ServicePackageResourceTags{}, }, + { + Factory: newResourceApplicationAccessScope, + Name: "Application Access Scope", + }, { Factory: newResourceApplicationAssignment, Name: "Application Assignment", @@ -62,10 +66,6 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka Factory: ResourceAccountAssignment, TypeName: "aws_ssoadmin_account_assignment", }, - { - Factory: ResourceApplicationAccessScope, - TypeName: "aws_ssoadmin_application_access_scope", - }, { Factory: ResourceCustomerManagedPolicyAttachment, TypeName: "aws_ssoadmin_customer_managed_policy_attachment", From 8daa56f681dbd4aec5f0235291a10db4d3b0b4fa Mon Sep 17 00:00:00 2001 From: Daniel Rieske Date: Tue, 12 Dec 2023 18:36:42 +0100 Subject: [PATCH 08/11] feat: remove ineffective assignments --- internal/service/ssoadmin/application_access_scope.go | 6 +++--- .../service/ssoadmin/application_access_scope_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/service/ssoadmin/application_access_scope.go b/internal/service/ssoadmin/application_access_scope.go index 7e8dc6e1951d..7999ca4134fc 100644 --- a/internal/service/ssoadmin/application_access_scope.go +++ b/internal/service/ssoadmin/application_access_scope.go @@ -122,7 +122,7 @@ func (r *resourceApplicationAccessScope) Read(ctx context.Context, req resource. return } - applicationARN, scope, err := ApplicationAccessScopeParseResourceID(state.ID.ValueString()) + applicationARN, scope, _ := ApplicationAccessScopeParseResourceID(state.ID.ValueString()) out, err := findApplicationAccessScopeByID(ctx, conn, applicationARN, scope) if tfresource.NotFound(err) { @@ -157,13 +157,13 @@ func (r *resourceApplicationAccessScope) Delete(ctx context.Context, req resourc return } - applicationARN, scope, err := ApplicationAccessScopeParseResourceID(state.ID.ValueString()) + applicationARN, scope, _ := ApplicationAccessScopeParseResourceID(state.ID.ValueString()) in := &ssoadmin.DeleteApplicationAccessScopeInput{ ApplicationArn: aws.String(applicationARN), Scope: aws.String(scope), } - _, err = conn.DeleteApplicationAccessScope(ctx, in) + _, err := conn.DeleteApplicationAccessScope(ctx, in) if err != nil { if errs.IsA[*awstypes.ResourceNotFoundException](err) { return diff --git a/internal/service/ssoadmin/application_access_scope_test.go b/internal/service/ssoadmin/application_access_scope_test.go index e49cc40ac31e..65a9bd11ad55 100644 --- a/internal/service/ssoadmin/application_access_scope_test.go +++ b/internal/service/ssoadmin/application_access_scope_test.go @@ -83,8 +83,8 @@ func testAccCheckApplicationAccessScopeDestroy(ctx context.Context) resource.Tes continue } - applicationARN, scope, err := tfssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) - _, err = tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, applicationARN, scope) + applicationARN, scope, _ := tfssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) + _, err := tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, applicationARN, scope) if errs.IsA[*types.ResourceNotFoundException](err) { return nil } @@ -112,8 +112,8 @@ func testAccCheckApplicationAccessScopeExists(ctx context.Context, name string) conn := acctest.Provider.Meta().(*conns.AWSClient).SSOAdminClient(ctx) - applicationARN, scope, err := tfssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) - _, err = tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, applicationARN, scope) + applicationARN, scope, _ := tfssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) + _, err := tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, applicationARN, scope) if err != nil { return create.Error(names.SSOAdmin, create.ErrActionCheckingExistence, tfssoadmin.ResNameApplicationAccessScope, rs.Primary.ID, err) } From 0580b86288af4ff705b17069cac930646ce641d0 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Mon, 18 Dec 2023 10:06:10 -0500 Subject: [PATCH 09/11] r/aws_ssoadmin_application_access_scope(doc): adjust id description --- website/docs/r/ssoadmin_application_access_scope.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/ssoadmin_application_access_scope.html.markdown b/website/docs/r/ssoadmin_application_access_scope.html.markdown index cf263e39d8f3..f00c51a95870 100644 --- a/website/docs/r/ssoadmin_application_access_scope.html.markdown +++ b/website/docs/r/ssoadmin_application_access_scope.html.markdown @@ -44,7 +44,7 @@ The following arguments are optional: This resource exports the following attributes in addition to the arguments above: -* `id` - ARN of the application. +* `id` - A comma-delimited string concatenating `application_arn` and `scope`. ## Import From 3931c1d5792941625c1435c940266713461305ee Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Mon, 18 Dec 2023 10:10:27 -0500 Subject: [PATCH 10/11] r/aws_ssoadmin_application_access_scope(test): add prechecks --- .../ssoadmin/application_access_scope_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/service/ssoadmin/application_access_scope_test.go b/internal/service/ssoadmin/application_access_scope_test.go index 65a9bd11ad55..dd0c9b00b75a 100644 --- a/internal/service/ssoadmin/application_access_scope_test.go +++ b/internal/service/ssoadmin/application_access_scope_test.go @@ -28,7 +28,11 @@ func TestAccSSOAdminApplicationAccessScope_basic(t *testing.T) { applicationResourceName := "aws_ssoadmin_application.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.SSOAdminEndpointID) + acctest.PreCheckSSOAdminInstances(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, names.SSOAdminEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckApplicationAccessScopeDestroy(ctx), @@ -57,7 +61,11 @@ func TestAccSSOAdminApplicationAccessScope_disappears(t *testing.T) { resourceName := "aws_ssoadmin_application_access_scope.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.SSOAdminEndpointID) + acctest.PreCheckSSOAdminInstances(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, names.SSOAdminEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckApplicationAccessScopeDestroy(ctx), From 6926b3f1a5013dcf23f1cdde6f6db8b3cfa2a337 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Mon, 18 Dec 2023 10:47:31 -0500 Subject: [PATCH 11/11] r/aws_ssoadmin_application_access_scope: prefer flex id functions, adjust finder --- .../ssoadmin/application_access_scope.go | 68 +++++++++++-------- .../ssoadmin/application_access_scope_test.go | 18 +---- 2 files changed, 40 insertions(+), 46 deletions(-) diff --git a/internal/service/ssoadmin/application_access_scope.go b/internal/service/ssoadmin/application_access_scope.go index 7999ca4134fc..e0b2a3f233c0 100644 --- a/internal/service/ssoadmin/application_access_scope.go +++ b/internal/service/ssoadmin/application_access_scope.go @@ -6,8 +6,6 @@ package ssoadmin import ( "context" "errors" - "fmt" - "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ssoadmin" @@ -22,6 +20,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" + intflex "github.com/hashicorp/terraform-provider-aws/internal/flex" "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" @@ -36,6 +35,8 @@ func newResourceApplicationAccessScope(_ context.Context) (resource.ResourceWith const ( ResNameApplicationAccessScope = "Application Access Scope" + + applicationAccessScopeIDPartCount = 2 ) type resourceApplicationAccessScope struct { @@ -108,7 +109,20 @@ func (r *resourceApplicationAccessScope) Create(ctx context.Context, req resourc return } - plan.ID = flex.StringToFramework(ctx, ApplicationAccessScopeCreateResourceID(plan.ApplicationARN.ValueString(), plan.Scope.ValueString())) + idParts := []string{ + plan.ApplicationARN.ValueString(), + plan.Scope.ValueString(), + } + id, err := intflex.FlattenResourceId(idParts, applicationAccessScopeIDPartCount, false) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.SSOAdmin, create.ErrActionCreating, ResNameApplicationAccessScope, plan.ApplicationARN.String(), err), + err.Error(), + ) + return + } + + plan.ID = types.StringValue(id) resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) } @@ -122,9 +136,7 @@ func (r *resourceApplicationAccessScope) Read(ctx context.Context, req resource. return } - applicationARN, scope, _ := ApplicationAccessScopeParseResourceID(state.ID.ValueString()) - - out, err := findApplicationAccessScopeByID(ctx, conn, applicationARN, scope) + out, err := findApplicationAccessScopeByID(ctx, conn, state.ID.ValueString()) if tfresource.NotFound(err) { resp.State.RemoveResource(ctx) return @@ -137,7 +149,18 @@ func (r *resourceApplicationAccessScope) Read(ctx context.Context, req resource. return } - state.ApplicationARN = flex.StringToFrameworkARN(ctx, aws.String(applicationARN)) + // ApplicationARN is not returned in the finder output. To allow import to set + // all attributes correctly, parse the ID for this value instead. + parts, err := intflex.ExpandResourceId(state.ID.ValueString(), applicationAccessScopeIDPartCount, false) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.SSOAdmin, create.ErrActionSetting, ResNameApplicationAccessScope, state.ID.String(), err), + err.Error(), + ) + return + } + + state.ApplicationARN = fwtypes.ARNValue(parts[0]) state.AuthorizedTargets = flex.FlattenFrameworkStringValueList(ctx, out.AuthorizedTargets) state.Scope = flex.StringToFramework(ctx, out.Scope) @@ -157,10 +180,9 @@ func (r *resourceApplicationAccessScope) Delete(ctx context.Context, req resourc return } - applicationARN, scope, _ := ApplicationAccessScopeParseResourceID(state.ID.ValueString()) in := &ssoadmin.DeleteApplicationAccessScopeInput{ - ApplicationArn: aws.String(applicationARN), - Scope: aws.String(scope), + ApplicationArn: aws.String(state.ApplicationARN.ValueString()), + Scope: aws.String(state.Scope.ValueString()), } _, err := conn.DeleteApplicationAccessScope(ctx, in) @@ -180,29 +202,15 @@ func (r *resourceApplicationAccessScope) ImportState(ctx context.Context, req re resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } -const applicationAccessScopeIDSeparator = "," - -func ApplicationAccessScopeCreateResourceID(applicationARN, scope string) *string { - parts := []string{applicationARN, scope} - id := strings.Join(parts, applicationAccessScopeIDSeparator) - - return &id -} - -func ApplicationAccessScopeParseResourceID(id string) (string, string, error) { - parts := strings.Split(id, applicationAccessScopeIDSeparator) - - if len(parts) == 2 && parts[0] != "" && parts[1] != "" { - return parts[0], parts[1], nil +func findApplicationAccessScopeByID(ctx context.Context, conn *ssoadmin.Client, id string) (*ssoadmin.GetApplicationAccessScopeOutput, error) { + parts, err := intflex.ExpandResourceId(id, applicationAccessScopeIDPartCount, false) + if err != nil { + return nil, err } - return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected APPLICATION_ARN%[2]sSCOPE", id, applicationAccessScopeIDSeparator) -} - -func findApplicationAccessScopeByID(ctx context.Context, conn *ssoadmin.Client, applicationARN, scope string) (*ssoadmin.GetApplicationAccessScopeOutput, error) { in := &ssoadmin.GetApplicationAccessScopeInput{ - ApplicationArn: aws.String(applicationARN), - Scope: aws.String(scope), + ApplicationArn: aws.String(parts[0]), + Scope: aws.String(parts[1]), } out, err := conn.GetApplicationAccessScope(ctx, in) diff --git a/internal/service/ssoadmin/application_access_scope_test.go b/internal/service/ssoadmin/application_access_scope_test.go index dd0c9b00b75a..1caf9bed6852 100644 --- a/internal/service/ssoadmin/application_access_scope_test.go +++ b/internal/service/ssoadmin/application_access_scope_test.go @@ -48,7 +48,6 @@ func TestAccSSOAdminApplicationAccessScope_basic(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateIdFunc: testAccApplicationAccessScopeImportStateIdFunc(resourceName), ImportStateVerify: true, }, }, @@ -91,8 +90,7 @@ func testAccCheckApplicationAccessScopeDestroy(ctx context.Context) resource.Tes continue } - applicationARN, scope, _ := tfssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) - _, err := tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, applicationARN, scope) + _, err := tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, rs.Primary.ID) if errs.IsA[*types.ResourceNotFoundException](err) { return nil } @@ -120,8 +118,7 @@ func testAccCheckApplicationAccessScopeExists(ctx context.Context, name string) conn := acctest.Provider.Meta().(*conns.AWSClient).SSOAdminClient(ctx) - applicationARN, scope, _ := tfssoadmin.ApplicationAccessScopeParseResourceID(rs.Primary.ID) - _, err := tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, applicationARN, scope) + _, err := tfssoadmin.FindApplicationAccessScopeByID(ctx, conn, rs.Primary.ID) if err != nil { return create.Error(names.SSOAdmin, create.ErrActionCheckingExistence, tfssoadmin.ResNameApplicationAccessScope, rs.Primary.ID, err) } @@ -130,17 +127,6 @@ func testAccCheckApplicationAccessScopeExists(ctx context.Context, name string) } } -func testAccApplicationAccessScopeImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return "", fmt.Errorf("Not Found: %s", resourceName) - } - - return fmt.Sprintf("%s,%s", rs.Primary.Attributes["application_arn"], rs.Primary.Attributes["scope"]), nil - } -} - func testAccApplicationAccessScopeConfig_basic(rName, scope string) string { return fmt.Sprintf(` data "aws_ssoadmin_instances" "test" {}