From 025f54ec7309a686cbf2a0ec328a927faa77eb83 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 19 May 2021 20:24:55 -0400 Subject: [PATCH 01/10] i/r/servicecat_tag_option_resource_assoc: Add finder --- .../service/servicecatalog/finder/finder.go | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/aws/internal/service/servicecatalog/finder/finder.go b/aws/internal/service/servicecatalog/finder/finder.go index 497218da55ba..791578044793 100644 --- a/aws/internal/service/servicecatalog/finder/finder.go +++ b/aws/internal/service/servicecatalog/finder/finder.go @@ -98,3 +98,32 @@ func BudgetResourceAssociation(conn *servicecatalog.ServiceCatalog, budgetName, return result, err } + +func TagOptionResourceAssociation(conn *servicecatalog.ServiceCatalog, tagOptionID, resourceID string) (*servicecatalog.ResourceDetail, error) { + input := &servicecatalog.ListResourcesForTagOptionInput{ + TagOptionId: aws.String(tagOptionID), + } + + var result *servicecatalog.ResourceDetail + + err := conn.ListResourcesForTagOptionPages(input, func(page *servicecatalog.ListResourcesForTagOptionOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, deet := range page.ResourceDetails { + if deet == nil { + continue + } + + if aws.StringValue(deet.Id) == resourceID { + result = deet + return false + } + } + + return !lastPage + }) + + return result, err +} From c2de85ccd5308e0a58b0e08d314da16bf68ed3f7 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 19 May 2021 20:25:27 -0400 Subject: [PATCH 02/10] i/r/servicecat_tag_option_resource_assoc: Add id funcs --- aws/internal/service/servicecatalog/id.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/aws/internal/service/servicecatalog/id.go b/aws/internal/service/servicecatalog/id.go index 1aa89da27896..14bfac365e6d 100644 --- a/aws/internal/service/servicecatalog/id.go +++ b/aws/internal/service/servicecatalog/id.go @@ -46,3 +46,17 @@ func BudgetResourceAssociationParseID(id string) (string, string, error) { func BudgetResourceAssociationID(budgetName, resourceID string) string { return strings.Join([]string{budgetName, resourceID}, ":") } + +func TagOptionResourceAssociationParseID(id string) (string, string, error) { + parts := strings.SplitN(id, ":", 2) + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%s), tagOptionID:resourceID", id) + } + + return parts[0], parts[1], nil +} + +func TagOptionResourceAssociationID(tagOptionID, resourceID string) string { + return strings.Join([]string{tagOptionID, resourceID}, ":") +} From 66f3942fc7f557ac526df1b8669848d547d6513e Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 19 May 2021 20:25:53 -0400 Subject: [PATCH 03/10] i/r/servicecat_tag_option_resource_assoc: Add waiter --- .../service/servicecatalog/waiter/status.go | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/aws/internal/service/servicecatalog/waiter/status.go b/aws/internal/service/servicecatalog/waiter/status.go index ad804e572e66..2ea72602cc7c 100644 --- a/aws/internal/service/servicecatalog/waiter/status.go +++ b/aws/internal/service/servicecatalog/waiter/status.go @@ -250,3 +250,27 @@ func BudgetResourceAssociationStatus(conn *servicecatalog.ServiceCatalog, budget return output, servicecatalog.StatusAvailable, err } } + +func TagOptionResourceAssociationStatus(conn *servicecatalog.ServiceCatalog, tagOptionID, resourceID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.TagOptionResourceAssociation(conn, tagOptionID, resourceID) + + if tfawserr.ErrCodeEquals(err, servicecatalog.ErrCodeResourceNotFoundException) { + return nil, StatusNotFound, &resource.NotFoundError{ + Message: fmt.Sprintf("tag option resource association not found (%s): %s", tfservicecatalog.TagOptionResourceAssociationID(tagOptionID, resourceID), err), + } + } + + if err != nil { + return nil, servicecatalog.StatusFailed, fmt.Errorf("error describing tag option resource association: %w", err) + } + + if output == nil { + return nil, StatusNotFound, &resource.NotFoundError{ + Message: fmt.Sprintf("finding tag option resource association (%s): empty response", tfservicecatalog.TagOptionResourceAssociationID(tagOptionID, resourceID)), + } + } + + return output, servicecatalog.StatusAvailable, err + } +} From 4dae28ce30a20ecc4038d30b9df54249dd35cb61 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 19 May 2021 20:26:08 -0400 Subject: [PATCH 04/10] i/r/servicecat_tag_option_resource_assoc: Add waiter --- .../service/servicecatalog/waiter/waiter.go | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/aws/internal/service/servicecatalog/waiter/waiter.go b/aws/internal/service/servicecatalog/waiter/waiter.go index 530a0629f7c1..3d8b4293b7c7 100644 --- a/aws/internal/service/servicecatalog/waiter/waiter.go +++ b/aws/internal/service/servicecatalog/waiter/waiter.go @@ -32,6 +32,9 @@ const ( BudgetResourceAssociationReadyTimeout = 3 * time.Minute BudgetResourceAssociationDeleteTimeout = 3 * time.Minute + TagOptionResourceAssociationReadyTimeout = 3 * time.Minute + TagOptionResourceAssociationDeleteTimeout = 3 * time.Minute + StatusNotFound = "NOT_FOUND" StatusUnavailable = "UNAVAILABLE" @@ -333,3 +336,33 @@ func BudgetResourceAssociationDeleted(conn *servicecatalog.ServiceCatalog, budge return err } + +func TagOptionResourceAssociationReady(conn *servicecatalog.ServiceCatalog, tagOptionID, resourceID string) (*servicecatalog.ResourceDetail, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{StatusNotFound, StatusUnavailable}, + Target: []string{servicecatalog.StatusAvailable}, + Refresh: TagOptionResourceAssociationStatus(conn, tagOptionID, resourceID), + Timeout: TagOptionResourceAssociationReadyTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*servicecatalog.ResourceDetail); ok { + return output, err + } + + return nil, err +} + +func TagOptionResourceAssociationDeleted(conn *servicecatalog.ServiceCatalog, tagOptionID, resourceID string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{servicecatalog.StatusAvailable}, + Target: []string{StatusNotFound, StatusUnavailable}, + Refresh: TagOptionResourceAssociationStatus(conn, tagOptionID, resourceID), + Timeout: TagOptionResourceAssociationDeleteTimeout, + } + + _, err := stateConf.WaitForState() + + return err +} From 82e14867f90b5e4b957c6699b05b8afa1fbd909b Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 19 May 2021 20:26:37 -0400 Subject: [PATCH 05/10] provider: Add resource --- aws/provider.go | 1 + 1 file changed, 1 insertion(+) diff --git a/aws/provider.go b/aws/provider.go index 4f7d14ec632f..ffb54b872c7a 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -1029,6 +1029,7 @@ func Provider() *schema.Provider { "aws_servicecatalog_product": resourceAwsServiceCatalogProduct(), "aws_servicecatalog_service_action": resourceAwsServiceCatalogServiceAction(), "aws_servicecatalog_tag_option": resourceAwsServiceCatalogTagOption(), + "aws_servicecatalog_tag_option_resource_association": resourceAwsServiceCatalogTagOptionResourceAssociation(), "aws_servicecatalog_product_portfolio_association": resourceAwsServiceCatalogProductPortfolioAssociation(), "aws_service_discovery_http_namespace": resourceAwsServiceDiscoveryHttpNamespace(), "aws_service_discovery_private_dns_namespace": resourceAwsServiceDiscoveryPrivateDnsNamespace(), From fb68ce7c029cbdcc89f0e6b3c9f8903bdf7b9886 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 19 May 2021 20:27:37 -0400 Subject: [PATCH 06/10] r/servicecat_tag_option_resource_assoc: New resource --- ...catalog_tag_option_resource_association.go | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 aws/resource_aws_servicecatalog_tag_option_resource_association.go diff --git a/aws/resource_aws_servicecatalog_tag_option_resource_association.go b/aws/resource_aws_servicecatalog_tag_option_resource_association.go new file mode 100644 index 000000000000..1b1678d37215 --- /dev/null +++ b/aws/resource_aws_servicecatalog_tag_option_resource_association.go @@ -0,0 +1,170 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/servicecatalog" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" + tfservicecatalog "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/servicecatalog" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/servicecatalog/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func resourceAwsServiceCatalogTagOptionResourceAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsServiceCatalogTagOptionResourceAssociationCreate, + Read: resourceAwsServiceCatalogTagOptionResourceAssociationRead, + Delete: resourceAwsServiceCatalogTagOptionResourceAssociationDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "resource_arn": { + Type: schema.TypeString, + Computed: true, + }, + "resource_created_time": { + Type: schema.TypeString, + Computed: true, + }, + "resource_description": { + Type: schema.TypeString, + Computed: true, + }, + "resource_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "resource_name": { + Type: schema.TypeString, + Computed: true, + }, + "tag_option_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsServiceCatalogTagOptionResourceAssociationCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).scconn + + input := &servicecatalog.AssociateTagOptionWithResourceInput{ + ResourceId: aws.String(d.Get("resource_id").(string)), + TagOptionId: aws.String(d.Get("tag_option_id").(string)), + } + + var output *servicecatalog.AssociateTagOptionWithResourceOutput + err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + var err error + + output, err = conn.AssociateTagOptionWithResource(input) + + if tfawserr.ErrMessageContains(err, servicecatalog.ErrCodeInvalidParametersException, "profile does not exist") { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + output, err = conn.AssociateTagOptionWithResource(input) + } + + if err != nil { + return fmt.Errorf("error associating Service Catalog Tag Option with Resource: %w", err) + } + + if output == nil { + return fmt.Errorf("error creating Service Catalog Tag Option Resource Association: empty response") + } + + d.SetId(tfservicecatalog.TagOptionResourceAssociationID(d.Get("tag_option_id").(string), d.Get("resource_id").(string))) + + return resourceAwsServiceCatalogTagOptionResourceAssociationRead(d, meta) +} + +func resourceAwsServiceCatalogTagOptionResourceAssociationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).scconn + + tagOptionID, resourceID, err := tfservicecatalog.TagOptionResourceAssociationParseID(d.Id()) + + if err != nil { + return fmt.Errorf("could not parse ID (%s): %w", d.Id(), err) + } + + output, err := waiter.TagOptionResourceAssociationReady(conn, tagOptionID, resourceID) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Service Catalog Tag Option Resource Association (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error describing Service Catalog Tag Option Resource Association (%s): %w", d.Id(), err) + } + + if output == nil { + return fmt.Errorf("error getting Service Catalog Tag Option Resource Association (%s): empty response", d.Id()) + } + + if output.CreatedTime != nil { + d.Set("resource_created_time", output.CreatedTime.Format(time.RFC3339)) + } + + d.Set("resource_arn", output.ARN) + d.Set("resource_description", output.Description) + d.Set("resource_id", output.Id) + d.Set("resource_name", output.Name) + d.Set("tag_option_id", tagOptionID) + + return nil +} + +func resourceAwsServiceCatalogTagOptionResourceAssociationDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).scconn + + tagOptionID, resourceID, err := tfservicecatalog.TagOptionResourceAssociationParseID(d.Id()) + + if err != nil { + return fmt.Errorf("could not parse ID (%s): %w", d.Id(), err) + } + + input := &servicecatalog.DisassociateTagOptionFromResourceInput{ + ResourceId: aws.String(resourceID), + TagOptionId: aws.String(tagOptionID), + } + + _, err = conn.DisassociateTagOptionFromResource(input) + + if tfawserr.ErrCodeEquals(err, servicecatalog.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("error disassociating Service Catalog Tag Option from Resource (%s): %w", d.Id(), err) + } + + err = waiter.TagOptionResourceAssociationDeleted(conn, tagOptionID, resourceID) + + if err != nil && !tfresource.NotFound(err) { + return fmt.Errorf("error waiting for Service Catalog Tag Option Resource Disassociation (%s): %w", d.Id(), err) + } + + return nil +} From 5b188d9fea228d845a15f555177eb2a067ff8a89 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 19 May 2021 20:28:11 -0400 Subject: [PATCH 07/10] tests/r/servicecat_tag_option_resource_assoc: New resource --- ...og_tag_option_resource_association_test.go | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 aws/resource_aws_servicecatalog_tag_option_resource_association_test.go diff --git a/aws/resource_aws_servicecatalog_tag_option_resource_association_test.go b/aws/resource_aws_servicecatalog_tag_option_resource_association_test.go new file mode 100644 index 000000000000..296307055d7e --- /dev/null +++ b/aws/resource_aws_servicecatalog_tag_option_resource_association_test.go @@ -0,0 +1,220 @@ +package aws + +import ( + "fmt" + "log" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/servicecatalog" + multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + tfservicecatalog "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/servicecatalog" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/servicecatalog/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +// add sweeper to delete known test servicecat tag option resource associations +func init() { + resource.AddTestSweepers("aws_servicecatalog_tag_option_resource_association", &resource.Sweeper{ + Name: "aws_servicecatalog_tag_option_resource_association", + Dependencies: []string{}, + F: testSweepServiceCatalogTagOptionResourceAssociations, + }) +} + +func testSweepServiceCatalogTagOptionResourceAssociations(region string) error { + client, err := sharedClientForRegion(region) + + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + + conn := client.(*AWSClient).scconn + sweepResources := make([]*testSweepResource, 0) + var errs *multierror.Error + + input := &servicecatalog.ListTagOptionsInput{} + + err = conn.ListTagOptionsPages(input, func(page *servicecatalog.ListTagOptionsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, tag := range page.TagOptionDetails { + if tag == nil { + continue + } + + resInput := &servicecatalog.ListResourcesForTagOptionInput{ + TagOptionId: tag.Id, + } + + err = conn.ListResourcesForTagOptionPages(resInput, func(page *servicecatalog.ListResourcesForTagOptionOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, resource := range page.ResourceDetails { + if resource == nil { + continue + } + + r := resourceAwsServiceCatalogTagOptionResourceAssociation() + d := r.Data(nil) + d.SetId(aws.StringValue(resource.Id)) + + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) + } + + return !lastPage + }) + } + + return !lastPage + }) + + if err != nil { + errs = multierror.Append(errs, fmt.Errorf("error describing Service Catalog Tag Option Resource Associations for %s: %w", region, err)) + } + + if err = testSweepResourceOrchestrator(sweepResources); err != nil { + errs = multierror.Append(errs, fmt.Errorf("error sweeping Service Catalog Tag Option Resource Associations for %s: %w", region, err)) + } + + if testSweepSkipSweepError(errs.ErrorOrNil()) { + log.Printf("[WARN] Skipping Service Catalog Tag Option Resource Associations sweep for %s: %s", region, errs) + return nil + } + + return errs.ErrorOrNil() +} + +func TestAccAWSServiceCatalogTagOptionResourceAssociation_basic(t *testing.T) { + resourceName := "aws_servicecatalog_tag_option_resource_association.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, servicecatalog.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsServiceCatalogTagOptionResourceAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSServiceCatalogTagOptionResourceAssociationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsServiceCatalogTagOptionResourceAssociationExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "resource_id", "aws_servicecatalog_portfolio.test", "id"), + resource.TestCheckResourceAttrPair(resourceName, "tag_option_id", "aws_servicecatalog_tag_option.test", "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSServiceCatalogTagOptionResourceAssociation_disappears(t *testing.T) { + resourceName := "aws_servicecatalog_tag_option_resource_association.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, servicecatalog.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsServiceCatalogTagOptionResourceAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSServiceCatalogTagOptionResourceAssociationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsServiceCatalogTagOptionResourceAssociationExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsServiceCatalogTagOptionResourceAssociation(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAwsServiceCatalogTagOptionResourceAssociationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).scconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_servicecatalog_tag_option_resource_association" { + continue + } + + tagOptionID, resourceID, err := tfservicecatalog.TagOptionResourceAssociationParseID(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("could not parse ID (%s): %w", rs.Primary.ID, err) + } + + err = waiter.TagOptionResourceAssociationDeleted(conn, tagOptionID, resourceID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return fmt.Errorf("waiting for Service Catalog Tag Option Resource Association to be destroyed (%s): %w", rs.Primary.ID, err) + } + } + + return nil +} + +func testAccCheckAwsServiceCatalogTagOptionResourceAssociationExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + + if !ok { + return fmt.Errorf("resource not found: %s", resourceName) + } + + tagOptionID, resourceID, err := tfservicecatalog.TagOptionResourceAssociationParseID(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("could not parse ID (%s): %w", rs.Primary.ID, err) + } + + conn := testAccProvider.Meta().(*AWSClient).scconn + + _, err = waiter.TagOptionResourceAssociationReady(conn, tagOptionID, resourceID) + + if err != nil { + return fmt.Errorf("waiting for Service Catalog Tag Option Resource Association existence (%s): %w", rs.Primary.ID, err) + } + + return nil + } +} + +func testAccAWSServiceCatalogTagOptionResourceAssociationConfig_base(rName string) string { + return fmt.Sprintf(` +resource "aws_servicecatalog_portfolio" "test" { + name = %[1]q + description = %[1]q + provider_name = %[1]q +} + +resource "aws_servicecatalog_tag_option" "test" { + key = %[1]q + value = %[1]q +} +`, rName) +} + +func testAccAWSServiceCatalogTagOptionResourceAssociationConfig_basic(rName string) string { + return composeConfig(testAccAWSServiceCatalogTagOptionResourceAssociationConfig_base(rName), ` +resource "aws_servicecatalog_tag_option_resource_association" "test" { + resource_id = aws_servicecatalog_portfolio.test.id + tag_option_id = aws_servicecatalog_tag_option.test.id +} +`) +} From c116566094664cf4ccf64a473b8df8d4c2410d1b Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Wed, 19 May 2021 20:28:54 -0400 Subject: [PATCH 08/10] docs/r/servicecat_tag_option_resource_assoc: New resource --- ..._option_resource_association.html.markdown | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 website/docs/r/servicecatalog_tag_option_resource_association.html.markdown diff --git a/website/docs/r/servicecatalog_tag_option_resource_association.html.markdown b/website/docs/r/servicecatalog_tag_option_resource_association.html.markdown new file mode 100644 index 000000000000..076f525d4f3e --- /dev/null +++ b/website/docs/r/servicecatalog_tag_option_resource_association.html.markdown @@ -0,0 +1,49 @@ +--- +subcategory: "Service Catalog" +layout: "aws" +page_title: "AWS: aws_servicecatalog_tag_option_resource_association" +description: |- + Manages a Service Catalog Tag Option Resource Association +--- + +# Resource: aws_servicecatalog_tag_option_resource_association + +Manages a Service Catalog Tag Option Resource Association. + +-> A "resource" is either a Service Catalog portfolio or product. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_servicecatalog_tag_option_resource_association" "example" { + resource_id = "prod-dnigbtea24ste" + tag_option_id = "tag-pjtvyakdlyo3m" +} +``` + +## Argument Reference + +The following arguments are required: + +* `resource_id` - (Required) Resource identifier. +* `tag_option_id` - (Required) Tag Option identifier. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - Identifier of the association. +* `resource_arn` - ARN of the resource. +* `resource_created_time` - Creation time of the resource. +* `resource_description` - Description of the resource. +* `resource_name` - Description of the resource. + +## Import + +`aws_servicecatalog_tag_option_resource_association` can be imported using the tag option ID and resource ID, e.g. + +``` +$ terraform import aws_servicecatalog_tag_option_resource_association.example tag-pjtvyakdlyo3m:prod-dnigbtea24ste +``` From 80227905a4afeec2c433c4d7d049f5a8b1212e07 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 20 May 2021 12:31:21 -0400 Subject: [PATCH 09/10] r/servicecatalog_tag_opt_res_assoc: Add changelog --- .changelog/19448.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/19448.txt diff --git a/.changelog/19448.txt b/.changelog/19448.txt new file mode 100644 index 000000000000..6b7ca1507972 --- /dev/null +++ b/.changelog/19448.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_servicecatalog_tag_option_resource_association +``` \ No newline at end of file From 4637efefff49354940658185ebf1ad8ef2d68e60 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 20 May 2021 12:31:41 -0400 Subject: [PATCH 10/10] docs/r/servicecatalog_tag_opt_res_assoc: Update --- ...servicecatalog_tag_option_resource_association.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/servicecatalog_tag_option_resource_association.html.markdown b/website/docs/r/servicecatalog_tag_option_resource_association.html.markdown index 076f525d4f3e..bb9f001d426e 100644 --- a/website/docs/r/servicecatalog_tag_option_resource_association.html.markdown +++ b/website/docs/r/servicecatalog_tag_option_resource_association.html.markdown @@ -10,7 +10,7 @@ description: |- Manages a Service Catalog Tag Option Resource Association. --> A "resource" is either a Service Catalog portfolio or product. +-> **Tip:** A "resource" is either a Service Catalog portfolio or product. ## Example Usage