diff --git a/.changelog/37066.txt b/.changelog/37066.txt new file mode 100644 index 000000000000..ea793f913d4c --- /dev/null +++ b/.changelog/37066.txt @@ -0,0 +1,7 @@ +```release-note:bug +resource/aws_servicecatalog_provisioned_product: Fixes error where tag values are not applied to products when tag values don't change. +``` + +```release-note:bug +resource/aws_servicecatalog_portfolio: Fixes error where deletion fails if resource was deleted out of band. +``` diff --git a/internal/acctest/s3.go b/internal/acctest/s3.go new file mode 100644 index 000000000000..f27fd049124b --- /dev/null +++ b/internal/acctest/s3.go @@ -0,0 +1,37 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package acctest + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfs3 "github.com/hashicorp/terraform-provider-aws/internal/service/s3" +) + +func S3BucketHasTag(ctx context.Context, bucketName, key, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := Provider.Meta().(*conns.AWSClient).S3Client(ctx) + + tags, err := tfs3.BucketListTags(ctx, conn, bucketName) + if err != nil { + return err + } + + for k, v := range tags { + if k == key { + if v.ValueString() == value { + return nil + } else { + return fmt.Errorf("expected tag %q value to be %s, got %s", key, value, v.ValueString()) + } + } + } + + return fmt.Errorf("expected tag %q not found", key) + } +} diff --git a/internal/generate/tagstests/file.tmpl b/internal/generate/tagstests/file.tmpl index 5d5eb70b9f99..972794fa2a95 100644 --- a/internal/generate/tagstests/file.tmpl +++ b/internal/generate/tagstests/file.tmpl @@ -51,6 +51,9 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/names" + {{ range .GoImports -}} + {{ if .Alias }}{{ .Alias }} {{ end }}"{{ .Path }}" + {{ end }} ) {{ if .Serialize }} @@ -173,6 +176,9 @@ func {{ template "testname" . }}_tags_AddOnUpdate(t *testing.T) { } func {{ template "testname" . }}_tags_EmptyTag_OnCreate(t *testing.T) { +{{- if .SkipEmptyTags }} + t.Skip("Resource {{ .Name }} does not support empty tags") +{{ end }} {{- template "Init" . }} resource.{{ if .Serialize }}Test{{ else }}ParallelTest{{ end }}(t, resource.TestCase{ @@ -200,6 +206,9 @@ func {{ template "testname" . }}_tags_EmptyTag_OnCreate(t *testing.T) { } func {{ template "testname" . }}_tags_EmptyTag_OnUpdate_Add(t *testing.T) { +{{- if .SkipEmptyTags }} + t.Skip("Resource {{ .Name }} does not support empty tags") +{{ end }} {{- template "Init" . }} resource.{{ if .Serialize }}Test{{ else }}ParallelTest{{ end }}(t, resource.TestCase{ @@ -237,6 +246,9 @@ func {{ template "testname" . }}_tags_EmptyTag_OnUpdate_Add(t *testing.T) { } func {{ template "testname" . }}_tags_EmptyTag_OnUpdate_Replace(t *testing.T) { +{{- if .SkipEmptyTags }} + t.Skip("Resource {{ .Name }} does not support empty tags") +{{ end }} {{- template "Init" . }} resource.{{ if .Serialize }}Test{{ else }}ParallelTest{{ end }}(t, resource.TestCase{ @@ -500,6 +512,9 @@ func {{ template "testname" . }}_tags_DefaultTags_updateToResourceOnly(t *testin } func {{ template "testname" . }}_tags_DefaultTags_emptyResourceTag(t *testing.T) { +{{- if .SkipEmptyTags }} + t.Skip("Resource {{ .Name }} does not support empty tags") +{{ end }} {{- template "Init" . }} resource.{{ if .Serialize }}Test{{ else }}ParallelTest{{ end }}(t, resource.TestCase{ diff --git a/internal/generate/tagstests/main.go b/internal/generate/tagstests/main.go index 330dd0a98425..68d85fe0d159 100644 --- a/internal/generate/tagstests/main.go +++ b/internal/generate/tagstests/main.go @@ -111,6 +111,13 @@ type ResourceDatum struct { Implementation implementation Serialize bool PreCheck bool + SkipEmptyTags bool // TODO: Remove when we have a strategy for resources that have a minimum tag value length of 1 + GoImports []goImport +} + +type goImport struct { + Path string + Alias string } //go:embed file.tmpl @@ -221,7 +228,28 @@ func (v *visitor) processFuncDecl(funcDecl *ast.FuncDecl) { d.ExistsTypeName = typeName } if attr, ok := args.Keyword["generator"]; ok { - d.Generator = attr + parts := strings.Split(attr, ";") + switch len(parts) { + case 1: + d.Generator = parts[0] + + case 2: + d.Generator = parts[1] + d.GoImports = append(d.GoImports, goImport{ + Path: parts[0], + }) + + case 3: + d.Generator = parts[2] + d.GoImports = append(d.GoImports, goImport{ + Path: parts[0], + Alias: parts[1], + }) + + default: + v.errs = append(v.errs, fmt.Errorf("invalid generator value: %q at %s.", attr, fmt.Sprintf("%s.%s", v.packageName, v.functionName))) + continue + } } if attr, ok := args.Keyword["importIgnore"]; ok { d.ImportIgnore = strings.Split(attr, ";") @@ -254,6 +282,14 @@ func (v *visitor) processFuncDecl(funcDecl *ast.FuncDecl) { skip = true } } + if attr, ok := args.Keyword["skipEmptyTags"]; ok { + if b, err := strconv.ParseBool(attr); err != nil { + v.errs = append(v.errs, fmt.Errorf("invalid skipEmptyTags value: %q at %s. Should be boolean value.", attr, fmt.Sprintf("%s.%s", v.packageName, v.functionName))) + continue + } else { + d.SkipEmptyTags = b + } + } } } } diff --git a/internal/service/s3/exports.go b/internal/service/s3/exports.go index 170ee69e94e6..2f8e998064a6 100644 --- a/internal/service/s3/exports.go +++ b/internal/service/s3/exports.go @@ -7,4 +7,6 @@ package s3 var ( ResourceBucket = resourceBucket ResourceObject = resourceObject + + BucketListTags = bucketListTags ) diff --git a/internal/service/s3/exports_test.go b/internal/service/s3/exports_test.go index 7debae1d1fd7..7b62aa5ef346 100644 --- a/internal/service/s3/exports_test.go +++ b/internal/service/s3/exports_test.go @@ -28,7 +28,6 @@ var ( ResourceDirectoryBucket = newDirectoryBucketResource ResourceObjectCopy = resourceObjectCopy - BucketListTags = bucketListTags BucketUpdateTags = bucketUpdateTags BucketRegionalDomainName = bucketRegionalDomainName BucketWebsiteEndpointAndDomain = bucketWebsiteEndpointAndDomain diff --git a/internal/service/servicecatalog/generate.go b/internal/service/servicecatalog/generate.go index bf6c4db1fc5b..57574ebc5bcf 100644 --- a/internal/service/servicecatalog/generate.go +++ b/internal/service/servicecatalog/generate.go @@ -3,6 +3,7 @@ //go:generate go run ../../generate/tags/main.go -ServiceTagsSlice //go:generate go run ../../generate/servicepackage/main.go +//go:generate go run ../../generate/tagstests/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. package servicecatalog diff --git a/internal/service/servicecatalog/portfolio.go b/internal/service/servicecatalog/portfolio.go index 2a1e79d33003..204864d224f5 100644 --- a/internal/service/servicecatalog/portfolio.go +++ b/internal/service/servicecatalog/portfolio.go @@ -26,6 +26,7 @@ import ( // @SDKResource("aws_servicecatalog_portfolio", name="Portfolio") // @Tags +// @Testing(existsType="github.com/aws/aws-sdk-go/service/servicecatalog.DescribePortfolioOutput", generator="github.com/hashicorp/terraform-plugin-testing/helper/acctest;sdkacctest;sdkacctest.RandString(5)", skipEmptyTags=true) func ResourcePortfolio() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourcePortfolioCreate, @@ -185,6 +186,10 @@ func resourcePortfolioDelete(ctx context.Context, d *schema.ResourceData, meta i Id: aws.String(d.Id()), }) + if tfawserr.ErrCodeEquals(err, servicecatalog.ErrCodeResourceNotFoundException) { + return diags + } + if err != nil { return sdkdiag.AppendErrorf(diags, "deleting Service Catalog Portfolio (%s): %s", d.Id(), err) } diff --git a/internal/service/servicecatalog/portfolio_data_source_test.go b/internal/service/servicecatalog/portfolio_data_source_test.go index 8f7bf425b678..08159befbf97 100644 --- a/internal/service/servicecatalog/portfolio_data_source_test.go +++ b/internal/service/servicecatalog/portfolio_data_source_test.go @@ -22,7 +22,7 @@ func TestAccServiceCatalogPortfolioDataSource_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckServiceCatlaogPortfolioDestroy(ctx), + CheckDestroy: testAccCheckPortfolioDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccPortfolioDataSourceConfig_basic(rName), diff --git a/internal/service/servicecatalog/portfolio_tags_gen_test.go b/internal/service/servicecatalog/portfolio_tags_gen_test.go new file mode 100644 index 000000000000..3a13209896e1 --- /dev/null +++ b/internal/service/servicecatalog/portfolio_tags_gen_test.go @@ -0,0 +1,701 @@ +// Code generated by internal/generate/tagstests/main.go; DO NOT EDIT. + +package servicecatalog_test + +import ( + "testing" + + "github.com/aws/aws-sdk-go/service/servicecatalog" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccServiceCatalogPortfolio_tags(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPortfolioConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPortfolioConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPortfolioConfig_tags1(rName, "key2", "value2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPortfolioConfig_tags0(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_null(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPortfolioConfig_tagsNull(rName, "key1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPortfolioConfig_tags0(rName), + PlanOnly: true, + ExpectNonEmptyPlan: false, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_AddOnUpdate(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPortfolioConfig_tags0(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + Config: testAccPortfolioConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_EmptyTag_OnCreate(t *testing.T) { + t.Skip("Resource Portfolio does not support empty tags") + + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPortfolioConfig_tags1(rName, "key1", ""), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPortfolioConfig_tags0(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_EmptyTag_OnUpdate_Add(t *testing.T) { + t.Skip("Resource Portfolio does not support empty tags") + + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPortfolioConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccPortfolioConfig_tags2(rName, "key1", "value1", "key2", ""), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPortfolioConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_EmptyTag_OnUpdate_Replace(t *testing.T) { + t.Skip("Resource Portfolio does not support empty tags") + + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPortfolioConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccPortfolioConfig_tags1(rName, "key1", ""), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_DefaultTags_providerOnly(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccPortfolioConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags2("key1", "value1updated", "key2", "value2"), + testAccPortfolioConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key2", "value2"), + testAccPortfolioConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags0(), + testAccPortfolioConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_DefaultTags_nonOverlapping(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"), + testAccPortfolioConfig_tags1(rName, "resourcekey1", "resourcevalue1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.resourcekey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey1", "resourcevalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1updated"), + testAccPortfolioConfig_tags2(rName, "resourcekey1", "resourcevalue1updated", "resourcekey2", "resourcevalue2"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.resourcekey1", "resourcevalue1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.resourcekey2", "resourcevalue2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey1", "resourcevalue1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey2", "resourcevalue2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags0(), + testAccPortfolioConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_DefaultTags_overlapping(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("overlapkey1", "providervalue1"), + testAccPortfolioConfig_tags1(rName, "overlapkey1", "resourcevalue1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags2("overlapkey1", "providervalue1", "overlapkey2", "providervalue2"), + testAccPortfolioConfig_tags2(rName, "overlapkey1", "resourcevalue1", "overlapkey2", "resourcevalue2"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey2", "resourcevalue2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey2", "resourcevalue2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("overlapkey1", "providervalue1"), + testAccPortfolioConfig_tags1(rName, "overlapkey1", "resourcevalue2"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_DefaultTags_updateToProviderOnly(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPortfolioConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccPortfolioConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_DefaultTags_updateToResourceOnly(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccPortfolioConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + Config: testAccPortfolioConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_DefaultTags_emptyResourceTag(t *testing.T) { + t.Skip("Resource Portfolio does not support empty tags") + + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccPortfolioConfig_tags1(rName, "key1", ""), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_DefaultTags_nullOverlappingResourceTag(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "providervalue1"), + testAccPortfolioConfig_tagsNull(rName, "key1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "providervalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceCatalogPortfolio_tags_DefaultTags_nullNonOverlappingResourceTag(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.DescribePortfolioOutput + resourceName := "aws_servicecatalog_portfolio.test" + rName := sdkacctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPortfolioDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"), + testAccPortfolioConfig_tagsNull(rName, "resourcekey1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckPortfolioExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/internal/service/servicecatalog/portfolio_test.go b/internal/service/servicecatalog/portfolio_test.go index 89483d93ed61..7cd4e8e32038 100644 --- a/internal/service/servicecatalog/portfolio_test.go +++ b/internal/service/servicecatalog/portfolio_test.go @@ -30,7 +30,7 @@ func TestAccServiceCatalogPortfolio_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckServiceCatlaogPortfolioDestroy(ctx), + CheckDestroy: testAccCheckPortfolioDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccPortfolioConfig_basic(name), @@ -63,7 +63,7 @@ func TestAccServiceCatalogPortfolio_disappears(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckServiceCatlaogPortfolioDestroy(ctx), + CheckDestroy: testAccCheckPortfolioDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccPortfolioConfig_basic(name), @@ -77,55 +77,6 @@ func TestAccServiceCatalogPortfolio_disappears(t *testing.T) { }) } -func TestAccServiceCatalogPortfolio_tags(t *testing.T) { - ctx := acctest.Context(t) - resourceName := "aws_servicecatalog_portfolio.test" - name := sdkacctest.RandString(5) - var dpo servicecatalog.DescribePortfolioOutput - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckServiceCatlaogPortfolioDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccPortfolioConfig_tags1(name, "key1", "value1"), - Check: resource.ComposeTestCheckFunc( - testAccCheckPortfolioExists(ctx, resourceName, &dpo), - resource.TestCheckResourceAttr(resourceName, "name", name), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccPortfolioConfig_tags2(name, "key1", "value1updated", "key2", "value2"), - Check: resource.ComposeTestCheckFunc( - testAccCheckPortfolioExists(ctx, resourceName, &dpo), - resource.TestCheckResourceAttr(resourceName, "name", name), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), - resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), - ), - }, - { - Config: testAccPortfolioConfig_tags1(name, "key2", "value2"), - Check: resource.ComposeTestCheckFunc( - testAccCheckPortfolioExists(ctx, resourceName, &dpo), - resource.TestCheckResourceAttr(resourceName, "name", name), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), - ), - }, - }, - }) -} - func testAccCheckPortfolioExists(ctx context.Context, n string, v *servicecatalog.DescribePortfolioOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -151,7 +102,7 @@ func testAccCheckPortfolioExists(ctx context.Context, n string, v *servicecatalo } } -func testAccCheckServiceCatlaogPortfolioDestroy(ctx context.Context) resource.TestCheckFunc { +func testAccCheckPortfolioDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceCatalogConn(ctx) @@ -187,6 +138,16 @@ resource "aws_servicecatalog_portfolio" "test" { `, name) } +func testAccPortfolioConfig_tags0(name string) string { + return fmt.Sprintf(` +resource "aws_servicecatalog_portfolio" "test" { + name = %[1]q + description = "test-b" + provider_name = "test-c" +} +`, name) +} + func testAccPortfolioConfig_tags1(name, tagKey1, tagValue1 string) string { return fmt.Sprintf(` resource "aws_servicecatalog_portfolio" "test" { @@ -215,3 +176,17 @@ resource "aws_servicecatalog_portfolio" "test" { } `, name, tagKey1, tagValue1, tagKey2, tagValue2) } + +func testAccPortfolioConfig_tagsNull(name, tagKey1 string) string { + return fmt.Sprintf(` +resource "aws_servicecatalog_portfolio" "test" { + name = %[1]q + description = "test-b" + provider_name = "test-c" + + tags = { + %[2]q = null + } +} +`, name, tagKey1) +} diff --git a/internal/service/servicecatalog/product.go b/internal/service/servicecatalog/product.go index c7dbc4734de9..b53f91f665fc 100644 --- a/internal/service/servicecatalog/product.go +++ b/internal/service/servicecatalog/product.go @@ -25,7 +25,8 @@ import ( ) // @SDKResource("aws_servicecatalog_product", name="Product") -// @Tags(identifierAttribute="id") +// @Tags +// @Testing(skipEmptyTags=true, importIgnore="accept_language;provisioning_artifact_parameters.0.disable_template_validation") func ResourceProduct() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceProductCreate, @@ -266,50 +267,62 @@ func resourceProductUpdate(ctx context.Context, d *schema.ResourceData, meta int var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ServiceCatalogConn(ctx) - if d.HasChangesExcept("tags", "tags_all") { - input := &servicecatalog.UpdateProductInput{ - Id: aws.String(d.Id()), - } + input := &servicecatalog.UpdateProductInput{ + Id: aws.String(d.Id()), + } - if v, ok := d.GetOk("accept_language"); ok { - input.AcceptLanguage = aws.String(v.(string)) - } + if v, ok := d.GetOk("accept_language"); ok { + input.AcceptLanguage = aws.String(v.(string)) + } - if v, ok := d.GetOk("description"); ok { - input.Description = aws.String(v.(string)) - } + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } - if v, ok := d.GetOk("distributor"); ok { - input.Distributor = aws.String(v.(string)) - } + if v, ok := d.GetOk("distributor"); ok { + input.Distributor = aws.String(v.(string)) + } - if v, ok := d.GetOk("name"); ok { - input.Name = aws.String(v.(string)) - } + if v, ok := d.GetOk("name"); ok { + input.Name = aws.String(v.(string)) + } - if v, ok := d.GetOk("owner"); ok { - input.Owner = aws.String(v.(string)) - } + if v, ok := d.GetOk("owner"); ok { + input.Owner = aws.String(v.(string)) + } - if v, ok := d.GetOk("support_description"); ok { - input.SupportDescription = aws.String(v.(string)) - } + if v, ok := d.GetOk("support_description"); ok { + input.SupportDescription = aws.String(v.(string)) + } + + if v, ok := d.GetOk("support_email"); ok { + input.SupportEmail = aws.String(v.(string)) + } + + if v, ok := d.GetOk("support_url"); ok { + input.SupportUrl = aws.String(v.(string)) + } - if v, ok := d.GetOk("support_email"); ok { - input.SupportEmail = aws.String(v.(string)) + if d.HasChange(names.AttrTagsAll) { + o, n := d.GetChange(names.AttrTagsAll) + oldTags := tftags.New(ctx, o) + newTags := tftags.New(ctx, n) + + if removedTags := oldTags.Removed(newTags).IgnoreSystem(names.ServiceCatalog); len(removedTags) > 0 { + input.RemoveTags = aws.StringSlice(removedTags.Keys()) } - if v, ok := d.GetOk("support_url"); ok { - input.SupportUrl = aws.String(v.(string)) + if updatedTags := oldTags.Updated(newTags).IgnoreSystem(names.ServiceCatalog); len(updatedTags) > 0 { + input.AddTags = Tags(updatedTags) } + } - _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, d.Timeout(schema.TimeoutUpdate), func() (interface{}, error) { - return conn.UpdateProductWithContext(ctx, input) - }, servicecatalog.ErrCodeInvalidParametersException, "profile does not exist") + _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, d.Timeout(schema.TimeoutUpdate), func() (interface{}, error) { + return conn.UpdateProductWithContext(ctx, input) + }, servicecatalog.ErrCodeInvalidParametersException, "profile does not exist") - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating Service Catalog Product (%s): %s", d.Id(), err) - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating Service Catalog Product (%s): %s", d.Id(), err) } return append(diags, resourceProductRead(ctx, d, meta)...) diff --git a/internal/service/servicecatalog/product_tags_gen_test.go b/internal/service/servicecatalog/product_tags_gen_test.go new file mode 100644 index 000000000000..fa7b8e140ca2 --- /dev/null +++ b/internal/service/servicecatalog/product_tags_gen_test.go @@ -0,0 +1,764 @@ +// Code generated by internal/generate/tagstests/main.go; DO NOT EDIT. + +package servicecatalog_test + +import ( + "testing" + + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccServiceCatalogProduct_tags(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: testAccProductConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: testAccProductConfig_tags1(rName, "key2", "value2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: testAccProductConfig_tags0(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_null(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProductConfig_tagsNull(rName, "key1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: testAccProductConfig_tags0(rName), + PlanOnly: true, + ExpectNonEmptyPlan: false, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_AddOnUpdate(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProductConfig_tags0(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + Config: testAccProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_EmptyTag_OnCreate(t *testing.T) { + t.Skip("Resource Product does not support empty tags") + + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProductConfig_tags1(rName, "key1", ""), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: testAccProductConfig_tags0(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_EmptyTag_OnUpdate_Add(t *testing.T) { + t.Skip("Resource Product does not support empty tags") + + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccProductConfig_tags2(rName, "key1", "value1", "key2", ""), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: testAccProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_EmptyTag_OnUpdate_Replace(t *testing.T) { + t.Skip("Resource Product does not support empty tags") + + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccProductConfig_tags1(rName, "key1", ""), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_DefaultTags_providerOnly(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags2("key1", "value1updated", "key2", "value2"), + testAccProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key2", "value2"), + testAccProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags0(), + testAccProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_DefaultTags_nonOverlapping(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"), + testAccProductConfig_tags1(rName, "resourcekey1", "resourcevalue1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.resourcekey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey1", "resourcevalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1updated"), + testAccProductConfig_tags2(rName, "resourcekey1", "resourcevalue1updated", "resourcekey2", "resourcevalue2"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.resourcekey1", "resourcevalue1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.resourcekey2", "resourcevalue2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey1", "resourcevalue1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey2", "resourcevalue2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags0(), + testAccProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_DefaultTags_overlapping(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("overlapkey1", "providervalue1"), + testAccProductConfig_tags1(rName, "overlapkey1", "resourcevalue1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags2("overlapkey1", "providervalue1", "overlapkey2", "providervalue2"), + testAccProductConfig_tags2(rName, "overlapkey1", "resourcevalue1", "overlapkey2", "resourcevalue2"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey2", "resourcevalue2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey2", "resourcevalue2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("overlapkey1", "providervalue1"), + testAccProductConfig_tags1(rName, "overlapkey1", "resourcevalue2"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_DefaultTags_updateToProviderOnly(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_DefaultTags_updateToResourceOnly(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + Config: testAccProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_DefaultTags_emptyResourceTag(t *testing.T) { + t.Skip("Resource Product does not support empty tags") + + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccProductConfig_tags1(rName, "key1", ""), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_DefaultTags_nullOverlappingResourceTag(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "providervalue1"), + testAccProductConfig_tagsNull(rName, "key1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "providervalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProduct_tags_DefaultTags_nullNonOverlappingResourceTag(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_servicecatalog_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"), + testAccProductConfig_tagsNull(rName, "resourcekey1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProductExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "provisioning_artifact_parameters.0.disable_template_validation", + }, + }, + }, + }) +} diff --git a/internal/service/servicecatalog/product_test.go b/internal/service/servicecatalog/product_test.go index 6326be356bb2..ce8bc393b307 100644 --- a/internal/service/servicecatalog/product_test.go +++ b/internal/service/servicecatalog/product_test.go @@ -138,40 +138,6 @@ func TestAccServiceCatalogProduct_update(t *testing.T) { }) } -func TestAccServiceCatalogProduct_updateTags(t *testing.T) { - ctx := acctest.Context(t) - resourceName := "aws_servicecatalog_product.test" - - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckProductDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccProductConfig_basic(rName, "beskrivning", "supportbeskrivning", domain, acctest.DefaultEmailAddress), - Check: resource.ComposeTestCheckFunc( - testAccCheckProductExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), - ), - }, - { - Config: testAccProductConfig_updateTags(rName, "beskrivning", "supportbeskrivning", domain, acctest.DefaultEmailAddress), - Check: resource.ComposeTestCheckFunc( - testAccCheckProductExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.Yak", rName), - resource.TestCheckResourceAttr(resourceName, "tags.Environment", "natural"), - ), - }, - }, - }) -} - func TestAccServiceCatalogProduct_physicalID(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_servicecatalog_product.test" @@ -337,19 +303,38 @@ resource "aws_servicecatalog_product" "test" { `, rName, description, supportDescription, domain, email)) } -func testAccProductConfig_updateTags(rName, description, supportDescription, domain, email string) string { +func testAccProductConfig_tags0(rName string) string { return acctest.ConfigCompose(testAccProductTemplateURLBaseConfig(rName), fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_servicecatalog_product" "test" { - description = %[2]q - distributor = "distributör" - name = %[1]q - owner = "ägare" - type = "CLOUD_FORMATION_TEMPLATE" - support_description = %[3]q - support_email = %[5]q - support_url = %[4]q + description = %[1]q + distributor = "distributör" + name = %[1]q + owner = "ägare" + type = "CLOUD_FORMATION_TEMPLATE" + + provisioning_artifact_parameters { + description = "artefaktbeskrivning" + disable_template_validation = true + name = %[1]q + template_url = "https://${aws_s3_bucket.test.bucket_regional_domain_name}/${aws_s3_object.test.key}" + type = "CLOUD_FORMATION_TEMPLATE" + } +} +`, rName)) +} + +func testAccProductConfig_tags1(rName, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose(testAccProductTemplateURLBaseConfig(rName), fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_servicecatalog_product" "test" { + description = %[1]q + distributor = "distributör" + name = %[1]q + owner = "ägare" + type = "CLOUD_FORMATION_TEMPLATE" provisioning_artifact_parameters { description = "artefaktbeskrivning" @@ -360,11 +345,63 @@ resource "aws_servicecatalog_product" "test" { } tags = { - Yak = %[1]q - Environment = "natural" + %[2]q = %[3]q } } -`, rName, description, supportDescription, domain, email)) +`, rName, tagKey1, tagValue1)) +} + +func testAccProductConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose(testAccProductTemplateURLBaseConfig(rName), fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_servicecatalog_product" "test" { + description = %[1]q + distributor = "distributör" + name = %[1]q + owner = "ägare" + type = "CLOUD_FORMATION_TEMPLATE" + + provisioning_artifact_parameters { + description = "artefaktbeskrivning" + disable_template_validation = true + name = %[1]q + template_url = "https://${aws_s3_bucket.test.bucket_regional_domain_name}/${aws_s3_object.test.key}" + type = "CLOUD_FORMATION_TEMPLATE" + } + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) +} + +func testAccProductConfig_tagsNull(rName, tagKey1 string) string { + return acctest.ConfigCompose(testAccProductTemplateURLBaseConfig(rName), fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_servicecatalog_product" "test" { + description = %[1]q + distributor = "distributör" + name = %[1]q + owner = "ägare" + type = "CLOUD_FORMATION_TEMPLATE" + + provisioning_artifact_parameters { + description = "artefaktbeskrivning" + disable_template_validation = true + name = %[1]q + template_url = "https://${aws_s3_bucket.test.bucket_regional_domain_name}/${aws_s3_object.test.key}" + type = "CLOUD_FORMATION_TEMPLATE" + } + + tags = { + %[2]q = null + } +} +`, rName, tagKey1)) } func testAccProductConfig_physicalID(rName, domain, email string) string { diff --git a/internal/service/servicecatalog/provisioned_product.go b/internal/service/servicecatalog/provisioned_product.go index daaf6c9b42d8..6a558d99cf1d 100644 --- a/internal/service/servicecatalog/provisioned_product.go +++ b/internal/service/servicecatalog/provisioned_product.go @@ -30,6 +30,7 @@ import ( // @SDKResource("aws_servicecatalog_provisioned_product", name="Provisioned Product") // @Tags +// @Testing(tagsTest=false, existsType="github.com/aws/aws-sdk-go/service/servicecatalog.ProvisionedProductDetail",importIgnore="accept_language;ignore_errors;provisioning_artifact_name;provisioning_parameters;retain_physical_resources", skipEmptyTags=true) func ResourceProvisionedProduct() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceProvisionedProductCreate, @@ -528,9 +529,9 @@ func resourceProvisionedProductUpdate(ctx context.Context, d *schema.ResourceDat input.ProvisioningPreferences = expandUpdateProvisioningPreferences(v.([]interface{})[0].(map[string]interface{})) } - if d.HasChanges("tags", "tags_all") { - input.Tags = getTagsIn(ctx) - } + // Send tags each time the resource is updated. This is necessary to automatically apply tags + // to provisioned AWS objects during update if the tags don't change. + input.Tags = getTagsIn(ctx) err := retry.RetryContext(ctx, d.Timeout(schema.TimeoutUpdate), func() *retry.RetryError { _, err := conn.UpdateProvisionedProductWithContext(ctx, input) diff --git a/internal/service/servicecatalog/provisioned_product_tags_test.go b/internal/service/servicecatalog/provisioned_product_tags_test.go new file mode 100644 index 000000000000..46f525aff34c --- /dev/null +++ b/internal/service/servicecatalog/provisioned_product_tags_test.go @@ -0,0 +1,780 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package servicecatalog_test + +import ( + "testing" + + "github.com/aws/aws-sdk-go/service/servicecatalog" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccServiceCatalogProvisionedProduct_tags(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProvisionedProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + { + Config: testAccProvisionedProductConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + // { + // Config: testAccProvisionedProductConfig_tags1(rName, "key2", "value2"), + // Check: resource.ComposeAggregateTestCheckFunc( + // testAccCheckProvisionedProductExists(ctx, resourceName, &v), + // resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + // resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + // ), + // }, + // { + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{ + // "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + // }, + // }, + // { + // Config: testAccProvisionedProductConfig_tags0(rName), + // Check: resource.ComposeAggregateTestCheckFunc( + // testAccCheckProvisionedProductExists(ctx, resourceName, &v), + // resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + // ), + // }, + // { + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{ + // "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + // }, + // }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_null(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProvisionedProductConfig_tagsNull(rName, "key1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + { + Config: testAccProvisionedProductConfig_tags0(rName), + PlanOnly: true, + ExpectNonEmptyPlan: false, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_AddOnUpdate(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProvisionedProductConfig_tags0(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + Config: testAccProvisionedProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_EmptyTag_OnCreate(t *testing.T) { + t.Skip("Resource ProvisionedProduct does not support empty tags") + + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProvisionedProductConfig_tags1(rName, "key1", ""), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + { + Config: testAccProvisionedProductConfig_tags0(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_EmptyTag_OnUpdate_Add(t *testing.T) { + t.Skip("Resource ProvisionedProduct does not support empty tags") + + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProvisionedProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccProvisionedProductConfig_tags2(rName, "key1", "value1", "key2", ""), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + { + Config: testAccProvisionedProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_EmptyTag_OnUpdate_Replace(t *testing.T) { + t.Skip("Resource ProvisionedProduct does not support empty tags") + + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProvisionedProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccProvisionedProductConfig_tags1(rName, "key1", ""), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_DefaultTags_providerOnly(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccProvisionedProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags2("key1", "value1updated", "key2", "value2"), + testAccProvisionedProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + // { + // Config: acctest.ConfigCompose( + // acctest.ConfigDefaultTags_Tags1("key2", "value2"), + // testAccProvisionedProductConfig_tags0(rName), + // ), + // Check: resource.ComposeAggregateTestCheckFunc( + // testAccCheckProvisionedProductExists(ctx, resourceName, &v), + // resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + // resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + // resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), + // ), + // }, + // { + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{ + // "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + // }, + // }, + // { + // Config: acctest.ConfigCompose( + // acctest.ConfigDefaultTags_Tags0(), + // testAccProvisionedProductConfig_tags0(rName), + // ), + // Check: resource.ComposeAggregateTestCheckFunc( + // testAccCheckProvisionedProductExists(ctx, resourceName, &v), + // resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + // resource.TestCheckResourceAttr(resourceName, "tags_all.%", "0"), + // ), + // }, + // { + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{ + // "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + // }, + // }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_DefaultTags_nonOverlapping(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"), + testAccProvisionedProductConfig_tags1(rName, "resourcekey1", "resourcevalue1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.resourcekey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey1", "resourcevalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1updated"), + testAccProvisionedProductConfig_tags2(rName, "resourcekey1", "resourcevalue1updated", "resourcekey2", "resourcevalue2"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.resourcekey1", "resourcevalue1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.resourcekey2", "resourcevalue2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey1", "resourcevalue1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.resourcekey2", "resourcevalue2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + // { + // Config: acctest.ConfigCompose( + // acctest.ConfigDefaultTags_Tags0(), + // testAccProvisionedProductConfig_tags0(rName), + // ), + // Check: resource.ComposeAggregateTestCheckFunc( + // testAccCheckProvisionedProductExists(ctx, resourceName, &v), + // resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + // resource.TestCheckResourceAttr(resourceName, "tags_all.%", "0"), + // ), + // }, + // { + // ResourceName: resourceName, + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{ + // "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + // }, + // }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_DefaultTags_overlapping(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("overlapkey1", "providervalue1"), + testAccProvisionedProductConfig_tags1(rName, "overlapkey1", "resourcevalue1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags2("overlapkey1", "providervalue1", "overlapkey2", "providervalue2"), + testAccProvisionedProductConfig_tags2(rName, "overlapkey1", "resourcevalue1", "overlapkey2", "resourcevalue2"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey2", "resourcevalue2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey2", "resourcevalue2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("overlapkey1", "providervalue1"), + testAccProvisionedProductConfig_tags1(rName, "overlapkey1", "resourcevalue2"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.overlapkey1", "resourcevalue2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.overlapkey1", "resourcevalue2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_DefaultTags_updateToProviderOnly(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccProvisionedProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccProvisionedProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_DefaultTags_updateToResourceOnly(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccProvisionedProductConfig_tags0(rName), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + Config: testAccProvisionedProductConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_DefaultTags_emptyResourceTag(t *testing.T) { + t.Skip("Resource ProvisionedProduct does not support empty tags") + + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "value1"), + testAccProvisionedProductConfig_tags1(rName, "key1", ""), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", ""), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_DefaultTags_nullOverlappingResourceTag(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("key1", "providervalue1"), + testAccProvisionedProductConfig_tagsNull(rName, "key1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "providervalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} + +func TestAccServiceCatalogProvisionedProduct_tags_DefaultTags_nullNonOverlappingResourceTag(t *testing.T) { + ctx := acctest.Context(t) + var v servicecatalog.ProvisionedProductDetail + resourceName := "aws_servicecatalog_provisioned_product.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: acctest.ConfigCompose( + acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"), + testAccProvisionedProductConfig_tagsNull(rName, "resourcekey1"), + ), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "accept_language", "ignore_errors", "provisioning_artifact_name", "provisioning_parameters", "retain_physical_resources", + }, + }, + }, + }) +} diff --git a/internal/service/servicecatalog/provisioned_product_test.go b/internal/service/servicecatalog/provisioned_product_test.go index 49ee97ffeffc..a13f095008e9 100644 --- a/internal/service/servicecatalog/provisioned_product_test.go +++ b/internal/service/servicecatalog/provisioned_product_test.go @@ -26,7 +26,6 @@ func TestAccServiceCatalogProvisionedProduct_basic(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_servicecatalog_provisioned_product.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) var pprod servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ @@ -36,7 +35,7 @@ func TestAccServiceCatalogProvisionedProduct_basic(t *testing.T) { CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_basic(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), + Config: testAccProvisionedProductConfig_basic(rName, "10.1.0.0/16"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), resource.TestCheckResourceAttr(resourceName, "accept_language", tfservicecatalog.AcceptLanguageEnglish), @@ -87,7 +86,6 @@ func TestAccServiceCatalogProvisionedProduct_update(t *testing.T) { resourceName := "aws_servicecatalog_provisioned_product.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) var pprod servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ @@ -97,13 +95,13 @@ func TestAccServiceCatalogProvisionedProduct_update(t *testing.T) { CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_basic(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), + Config: testAccProvisionedProductConfig_basic(rName, "10.1.0.0/16"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), ), }, { - Config: testAccProvisionedProductConfig_basic(rName, domain, acctest.DefaultEmailAddress, "10.10.0.0/16"), + Config: testAccProvisionedProductConfig_basic(rName, "10.10.0.0/16"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), resource.TestCheckResourceAttr(resourceName, "accept_language", tfservicecatalog.AcceptLanguageEnglish), @@ -151,7 +149,6 @@ func TestAccServiceCatalogProvisionedProduct_stackSetProvisioningPreferences(t * ctx := acctest.Context(t) resourceName := "aws_servicecatalog_provisioned_product.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) var pprod servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ @@ -161,7 +158,7 @@ func TestAccServiceCatalogProvisionedProduct_stackSetProvisioningPreferences(t * CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_stackSetprovisioningPreferences(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16", 1, 2), + Config: testAccProvisionedProductConfig_stackSetprovisioningPreferences(rName, "10.1.0.0/16", 1, 2), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -186,7 +183,7 @@ func TestAccServiceCatalogProvisionedProduct_stackSetProvisioningPreferences(t * }, }, { - Config: testAccProvisionedProductConfig_stackSetprovisioningPreferences(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16", 3, 4), + Config: testAccProvisionedProductConfig_stackSetprovisioningPreferences(rName, "10.1.0.0/16", 3, 4), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -198,7 +195,7 @@ func TestAccServiceCatalogProvisionedProduct_stackSetProvisioningPreferences(t * ), }, { - Config: testAccProvisionedProductConfig_basic(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), + Config: testAccProvisionedProductConfig_basic(rName, "10.1.0.0/16"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -216,7 +213,6 @@ func TestAccServiceCatalogProvisionedProduct_ProductName_update(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) productName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) productNameUpdated := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) var pprod servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ @@ -226,7 +222,7 @@ func TestAccServiceCatalogProvisionedProduct_ProductName_update(t *testing.T) { CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_productName(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16", productName), + Config: testAccProvisionedProductConfig_productName(rName, "10.1.0.0/16", productName), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), resource.TestCheckResourceAttrPair(resourceName, "product_name", "aws_servicecatalog_product.test", "name"), @@ -235,7 +231,7 @@ func TestAccServiceCatalogProvisionedProduct_ProductName_update(t *testing.T) { }, { // update the product name, but keep provisioned product name as-is to trigger an in-place update - Config: testAccProvisionedProductConfig_productName(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16", productNameUpdated), + Config: testAccProvisionedProductConfig_productName(rName, "10.1.0.0/16", productNameUpdated), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), resource.TestCheckResourceAttrPair(resourceName, "product_name", "aws_servicecatalog_product.test", "name"), @@ -268,7 +264,6 @@ func TestAccServiceCatalogProvisionedProduct_ProvisioningArtifactName_update(t * rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) artifactName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) var pprod1, pprod2 servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ @@ -279,14 +274,14 @@ func TestAccServiceCatalogProvisionedProduct_ProvisioningArtifactName_update(t * Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_basic(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), + Config: testAccProvisionedProductConfig_basic(rName, "10.1.0.0/16"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod1), resource.TestCheckResourceAttrPair(resourceName, "provisioning_artifact_name", productResourceName, "provisioning_artifact_parameters.0.name"), ), }, { - Config: testAccProvisionedProductConfig_ProvisionedArtifactName_update(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16", artifactName), + Config: testAccProvisionedProductConfig_ProvisionedArtifactName_update(rName, "10.1.0.0/16", artifactName), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod2), resource.TestCheckResourceAttrPair(resourceName, "provisioning_artifact_name", artifactResourceName, "name"), @@ -302,7 +297,6 @@ func TestAccServiceCatalogProvisionedProduct_computedOutputs(t *testing.T) { resourceName := "aws_servicecatalog_provisioned_product.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) var pprod servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ @@ -312,7 +306,7 @@ func TestAccServiceCatalogProvisionedProduct_computedOutputs(t *testing.T) { CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_computedOutputs(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), + Config: testAccProvisionedProductConfig_computedOutputs(rName, "10.1.0.0/16"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), resource.TestCheckResourceAttr(resourceName, "outputs.#", "3"), @@ -328,7 +322,7 @@ func TestAccServiceCatalogProvisionedProduct_computedOutputs(t *testing.T) { ), }, { - Config: testAccProvisionedProductConfig_computedOutputs(rName, domain, acctest.DefaultEmailAddress, "10.1.0.1/16"), + Config: testAccProvisionedProductConfig_computedOutputs(rName, "10.1.0.1/16"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), resource.TestCheckResourceAttr(resourceName, "outputs.#", "3"), @@ -352,7 +346,6 @@ func TestAccServiceCatalogProvisionedProduct_disappears(t *testing.T) { resourceName := "aws_servicecatalog_provisioned_product.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) var pprod servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ @@ -362,7 +355,7 @@ func TestAccServiceCatalogProvisionedProduct_disappears(t *testing.T) { CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_basic(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), + Config: testAccProvisionedProductConfig_basic(rName, "10.1.0.0/16"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfservicecatalog.ResourceProvisionedProduct(), resourceName), @@ -373,13 +366,10 @@ func TestAccServiceCatalogProvisionedProduct_disappears(t *testing.T) { }) } -func TestAccServiceCatalogProvisionedProduct_tags(t *testing.T) { +func TestAccServiceCatalogProvisionedProduct_errorOnCreate(t *testing.T) { ctx := acctest.Context(t) - resourceName := "aws_servicecatalog_provisioned_product.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) - var pprod servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -388,30 +378,18 @@ func TestAccServiceCatalogProvisionedProduct_tags(t *testing.T) { CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_tags(rName, "Name", rName, domain, acctest.DefaultEmailAddress), - Check: resource.ComposeTestCheckFunc( - testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), - ), - }, - { - Config: testAccProvisionedProductConfig_tags(rName, "NotName", rName, domain, acctest.DefaultEmailAddress), - Check: resource.ComposeTestCheckFunc( - testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.NotName", rName), - ), + Config: testAccProvisionedProductConfig_error(rName, "10.1.0.0/16"), + ExpectError: regexache.MustCompile(`AmazonCloudFormationException Unresolved resource dependencies \[MyVPC\] in the Outputs block of the template`), }, }, }) } -func TestAccServiceCatalogProvisionedProduct_errorOnCreate(t *testing.T) { +func TestAccServiceCatalogProvisionedProduct_errorOnUpdate(t *testing.T) { ctx := acctest.Context(t) - + resourceName := "aws_servicecatalog_provisioned_product.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) + var pprod servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -420,18 +398,31 @@ func TestAccServiceCatalogProvisionedProduct_errorOnCreate(t *testing.T) { CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_error(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), + Config: testAccProvisionedProductConfig_basic(rName, "10.1.0.0/16"), + Check: resource.ComposeTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), + ), + }, + { + Config: testAccProvisionedProductConfig_error(rName, "10.1.0.0/16"), ExpectError: regexache.MustCompile(`AmazonCloudFormationException Unresolved resource dependencies \[MyVPC\] in the Outputs block of the template`), }, + { + // Check we can still run a complete apply after the previous update error + Config: testAccProvisionedProductConfig_basic(rName, "10.1.0.0/16"), + Check: resource.ComposeTestCheckFunc( + testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), + ), + }, }, }) } -func TestAccServiceCatalogProvisionedProduct_errorOnUpdate(t *testing.T) { +func TestAccServiceCatalogProvisionedProduct_productTagUpdateAfterError(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_servicecatalog_provisioned_product.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domain := fmt.Sprintf("http://%s", acctest.RandomDomainName()) + bucketName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) var pprod servicecatalog.ProvisionedProductDetail resource.ParallelTest(t, resource.TestCase{ @@ -441,20 +432,25 @@ func TestAccServiceCatalogProvisionedProduct_errorOnUpdate(t *testing.T) { CheckDestroy: testAccCheckProvisionedProductDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccProvisionedProductConfig_basic(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), + Config: testAccProvisionedProductConfig_productTagUpdateAfterError_valid(rName, bucketName, "1.0"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.version", "1.0"), + acctest.S3BucketHasTag(ctx, bucketName, "version", "1.0"), ), }, { - Config: testAccProvisionedProductConfig_error(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), - ExpectError: regexache.MustCompile(`AmazonCloudFormationException Unresolved resource dependencies \[MyVPC\] in the Outputs block of the template`), + Config: testAccProvisionedProductConfig_productTagUpdateAfterError_confict(rName, bucketName, "1.5"), + ExpectError: regexache.MustCompile(`BucketAlreadyOwnedByYou`), }, { - // Check we can still run a complete apply after the previous update error - Config: testAccProvisionedProductConfig_basic(rName, domain, acctest.DefaultEmailAddress, "10.1.0.0/16"), + Config: testAccProvisionedProductConfig_productTagUpdateAfterError_valid(rName, bucketName, "1.5"), Check: resource.ComposeTestCheckFunc( testAccCheckProvisionedProductExists(ctx, resourceName, &pprod), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.version", "1.5"), + acctest.S3BucketHasTag(ctx, bucketName, "version", "1.5"), ), }, }, @@ -574,7 +570,7 @@ data "aws_servicecatalog_launch_paths" "test" { `, rName) } -func testAccProvisionedProductTemplateURLBaseConfig(rName, domain, email string) string { +func testAccProvisionedProductTemplateURLBaseConfig(rName string) string { return acctest.ConfigCompose( testAccProvisionedProductPortfolioBaseConfig(rName), fmt.Sprintf(` @@ -639,8 +635,6 @@ resource "aws_servicecatalog_product" "test" { owner = "ägare" type = "CLOUD_FORMATION_TEMPLATE" support_description = %[1]q - support_email = %[3]q - support_url = %[2]q provisioning_artifact_parameters { description = "artefaktbeskrivning" @@ -649,15 +643,62 @@ resource "aws_servicecatalog_product" "test" { template_url = "https://${aws_s3_bucket.test.bucket_regional_domain_name}/${aws_s3_object.test.key}" type = "CLOUD_FORMATION_TEMPLATE" } +} +`, rName)) +} - tags = { - Name = %[1]q +func testAccProvisionedProductTemplateURLSimpleBaseConfig(rName string) string { + return acctest.ConfigCompose( + testAccProvisionedProductPortfolioBaseConfig(rName), + fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q + force_destroy = true +} + +resource "aws_s3_object" "test" { + bucket = aws_s3_bucket.test.id + key = "%[1]s.json" + + content = jsonencode({ + AWSTemplateFormatVersion = "2010-09-09" + + Parameters = { + BucketName = { + Type = "String" + } + } + + Resources = { + MyS3Bucket = { + Type = "AWS::S3::Bucket" + Properties = { + BucketName = { Ref = "BucketName" } + } + } + } + }) +} + +resource "aws_servicecatalog_product" "test" { + description = %[1]q + distributor = "distributör" + name = %[1]q + owner = "ägare" + type = "CLOUD_FORMATION_TEMPLATE" + + provisioning_artifact_parameters { + description = "artefaktbeskrivning" + disable_template_validation = true + name = %[1]q + template_url = "https://${aws_s3_bucket.test.bucket_regional_domain_name}/${aws_s3_object.test.key}" + type = "CLOUD_FORMATION_TEMPLATE" } } -`, rName, domain, email)) +`, rName)) } -func testAccProvisionedProductPhysicalTemplateIDBaseConfig(rName, domain, email string) string { +func testAccProvisionedProductPhysicalTemplateIDBaseConfig(rName string) string { return acctest.ConfigCompose( testAccProvisionedProductPortfolioBaseConfig(rName), fmt.Sprintf(` @@ -725,8 +766,6 @@ resource "aws_servicecatalog_product" "test" { owner = "ägare" type = "CLOUD_FORMATION_TEMPLATE" support_description = %[1]q - support_email = %[3]q - support_url = %[2]q provisioning_artifact_parameters { description = "artefaktbeskrivning" @@ -740,11 +779,11 @@ resource "aws_servicecatalog_product" "test" { Name = %[1]q } } -`, rName, domain, email)) +`, rName)) } -func testAccProvisionedProductConfig_basic(rName, domain, email, vpcCidr string) string { - return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(rName, domain, email), +func testAccProvisionedProductConfig_basic(rName, vpcCidr string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(rName), fmt.Sprintf(` resource "aws_servicecatalog_provisioned_product" "test" { name = %[1]q @@ -761,12 +800,17 @@ resource "aws_servicecatalog_provisioned_product" "test" { key = "LeaveMeEmpty" value = "" } + + # Leave this here to test tag behavior on Update + tags = { + Name = %[1]q + } } `, rName, vpcCidr)) } -func testAccProvisionedProductConfig_computedOutputs(rName, domain, email, vpcCidr string) string { - return acctest.ConfigCompose(testAccProvisionedProductPhysicalTemplateIDBaseConfig(rName, domain, email), +func testAccProvisionedProductConfig_computedOutputs(rName, vpcCidr string) string { + return acctest.ConfigCompose(testAccProvisionedProductPhysicalTemplateIDBaseConfig(rName), fmt.Sprintf(` resource "aws_servicecatalog_provisioned_product" "test" { name = %[1]q @@ -787,8 +831,8 @@ resource "aws_servicecatalog_provisioned_product" "test" { `, rName, vpcCidr)) } -func testAccProvisionedProductConfig_stackSetprovisioningPreferences(rName, domain, email, vpcCidr string, failureToleranceCount, maxConcurrencyCount int) string { - return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(rName, domain, email), +func testAccProvisionedProductConfig_stackSetprovisioningPreferences(rName, vpcCidr string, failureToleranceCount, maxConcurrencyCount int) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(rName), fmt.Sprintf(` data "aws_region" "current" {} @@ -818,8 +862,8 @@ resource "aws_servicecatalog_provisioned_product" "test" { `, rName, vpcCidr, failureToleranceCount, maxConcurrencyCount)) } -func testAccProvisionedProductConfig_productName(rName, domain, email, vpcCidr, productName string) string { - return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(productName, domain, email), +func testAccProvisionedProductConfig_productName(rName, vpcCidr, productName string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(productName), fmt.Sprintf(` resource "aws_servicecatalog_provisioned_product" "test" { name = %[1]q @@ -840,8 +884,8 @@ resource "aws_servicecatalog_provisioned_product" "test" { `, rName, vpcCidr)) } -func testAccProvisionedProductConfig_ProvisionedArtifactName_update(rName, domain, email, vpcCidr, artifactName string) string { - return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(rName, domain, email), +func testAccProvisionedProductConfig_ProvisionedArtifactName_update(rName, vpcCidr, artifactName string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(rName), fmt.Sprintf(` resource "aws_servicecatalog_provisioning_artifact" "test" { product_id = aws_servicecatalog_product.test.id @@ -865,6 +909,11 @@ resource "aws_servicecatalog_provisioned_product" "test" { key = "LeaveMeEmpty" value = "" } + + # Leave this here to test tag behavior on Update + tags = { + Name = %[1]q + } } `, rName, vpcCidr, artifactName)) } @@ -872,8 +921,8 @@ resource "aws_servicecatalog_provisioned_product" "test" { // Because the `provisioning_parameter` "LeaveMeEmpty" is not empty, this configuration results in an error. // The `status_message` will be: // AmazonCloudFormationException Unresolved resource dependencies [MyVPC] in the Outputs block of the template -func testAccProvisionedProductConfig_error(rName, domain, email, vpcCidr string) string { - return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(rName, domain, email), +func testAccProvisionedProductConfig_error(rName, vpcCidr string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(rName), fmt.Sprintf(` resource "aws_servicecatalog_provisioned_product" "test" { name = %[1]q @@ -894,8 +943,54 @@ resource "aws_servicecatalog_provisioned_product" "test" { `, rName, vpcCidr)) } -func testAccProvisionedProductConfig_tags(rName, tagKey, tagValue, domain, email string) string { - return acctest.ConfigCompose(testAccProvisionedProductTemplateURLBaseConfig(rName, domain, email), +func testAccProvisionedProductConfig_productTagUpdateAfterError_valid(rName, bucketName, tagValue string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLSimpleBaseConfig(rName), + fmt.Sprintf(` +resource "aws_servicecatalog_provisioned_product" "test" { + name = %[1]q + product_id = aws_servicecatalog_product.test.id + provisioning_artifact_name = %[1]q + path_id = data.aws_servicecatalog_launch_paths.test.summaries[0].path_id + + provisioning_parameters { + key = "BucketName" + value = %[2]q + } + + tags = { + version = %[3]q + } +} +`, rName, bucketName, tagValue)) +} + +func testAccProvisionedProductConfig_productTagUpdateAfterError_confict(rName, conflictingBucketName, tagValue string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLSimpleBaseConfig(rName), + fmt.Sprintf(` +resource "aws_servicecatalog_provisioned_product" "test" { + name = %[1]q + product_id = aws_servicecatalog_product.test.id + provisioning_artifact_name = %[1]q + path_id = data.aws_servicecatalog_launch_paths.test.summaries[0].path_id + + provisioning_parameters { + key = "BucketName" + value = aws_s3_bucket.conflict.bucket + } + + tags = { + version = %[3]q + } +} + +resource "aws_s3_bucket" "conflict" { + bucket = %[2]q +} +`, rName, conflictingBucketName, tagValue)) +} + +func testAccProvisionedProductConfig_tags0(rName string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLSimpleBaseConfig(rName), fmt.Sprintf(` resource "aws_servicecatalog_provisioned_product" "test" { name = %[1]q @@ -904,18 +999,73 @@ resource "aws_servicecatalog_provisioned_product" "test" { path_id = data.aws_servicecatalog_launch_paths.test.summaries[0].path_id provisioning_parameters { - key = "VPCPrimaryCIDR" - value = "10.2.0.0/16" + key = "BucketName" + value = "%[1]s-dest" } +} +`, rName)) +} + +func testAccProvisionedProductConfig_tags1(rName, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLSimpleBaseConfig(rName), + fmt.Sprintf(` +resource "aws_servicecatalog_provisioned_product" "test" { + name = %[1]q + product_id = aws_servicecatalog_constraint.test.product_id + provisioning_artifact_name = %[1]q + path_id = data.aws_servicecatalog_launch_paths.test.summaries[0].path_id provisioning_parameters { - key = "LeaveMeEmpty" - value = "" + key = "BucketName" + value = "%[1]s-dest" } tags = { %[2]q = %[3]q } } -`, rName, tagKey, tagValue)) +`, rName, tagKey1, tagValue1)) +} + +func testAccProvisionedProductConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLSimpleBaseConfig(rName), + fmt.Sprintf(` +resource "aws_servicecatalog_provisioned_product" "test" { + name = %[1]q + product_id = aws_servicecatalog_constraint.test.product_id + provisioning_artifact_name = %[1]q + path_id = data.aws_servicecatalog_launch_paths.test.summaries[0].path_id + + provisioning_parameters { + key = "BucketName" + value = "%[1]s-dest" + } + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) +} + +func testAccProvisionedProductConfig_tagsNull(rName, tagKey1 string) string { + return acctest.ConfigCompose(testAccProvisionedProductTemplateURLSimpleBaseConfig(rName), + fmt.Sprintf(` +resource "aws_servicecatalog_provisioned_product" "test" { + name = %[1]q + product_id = aws_servicecatalog_constraint.test.product_id + provisioning_artifact_name = %[1]q + path_id = data.aws_servicecatalog_launch_paths.test.summaries[0].path_id + + provisioning_parameters { + key = "BucketName" + value = "%[1]s-dest" + } + + tags = { + %[2]q = null + } +} +`, rName, tagKey1)) } diff --git a/internal/service/servicecatalog/service_package_gen.go b/internal/service/servicecatalog/service_package_gen.go index f1a5e8aeebc8..bb0eb4609cd9 100644 --- a/internal/service/servicecatalog/service_package_gen.go +++ b/internal/service/servicecatalog/service_package_gen.go @@ -84,9 +84,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka Factory: ResourceProduct, TypeName: "aws_servicecatalog_product", Name: "Product", - Tags: &types.ServicePackageResourceTags{ - IdentifierAttribute: "id", - }, + Tags: &types.ServicePackageResourceTags{}, }, { Factory: ResourceProductPortfolioAssociation, diff --git a/internal/service/servicecatalog/tags.go b/internal/service/servicecatalog/tags.go index 6e494aee47f3..b5a82780a26c 100644 --- a/internal/service/servicecatalog/tags.go +++ b/internal/service/servicecatalog/tags.go @@ -8,43 +8,14 @@ package servicecatalog import ( "context" - "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/servicecatalog" - "github.com/aws/aws-sdk-go/service/servicecatalog/servicecatalogiface" - "github.com/hashicorp/terraform-provider-aws/internal/conns" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" - "github.com/hashicorp/terraform-provider-aws/names" ) // Custom Service Catalog tag service update functions using the same format as generated code. -func productUpdateTags(ctx context.Context, conn servicecatalogiface.ServiceCatalogAPI, identifier string, oldTagsMap, newTagsMap any) error { - oldTags := tftags.New(ctx, oldTagsMap) - newTags := tftags.New(ctx, newTagsMap) - - input := &servicecatalog.UpdateProductInput{ - Id: aws.String(identifier), - } - - if removedTags := oldTags.Removed(newTags).IgnoreSystem(names.ServiceCatalog); len(removedTags) > 0 { - input.RemoveTags = aws.StringSlice(removedTags.Keys()) - } - - if updatedTags := oldTags.Updated(newTags).IgnoreSystem(names.ServiceCatalog); len(updatedTags) > 0 { - input.AddTags = Tags(updatedTags) - } - - _, err := conn.UpdateProductWithContext(ctx, input) - - if err != nil { - return fmt.Errorf("updating tags for Service Catalog Product (%s): %w", identifier, err) - } - - return nil -} - func recordKeyValueTags(ctx context.Context, tags []*servicecatalog.RecordTag) tftags.KeyValueTags { m := make(map[string]*string, len(tags)) @@ -54,9 +25,3 @@ func recordKeyValueTags(ctx context.Context, tags []*servicecatalog.RecordTag) t return tftags.New(ctx, m) } - -// UpdateTags updates servicecatalog service tags. -// It is called from outside this package. -func (p *servicePackage) UpdateTags(ctx context.Context, meta any, identifier string, oldTags, newTags any) error { - return productUpdateTags(ctx, meta.(*conns.AWSClient).ServiceCatalogConn(ctx), identifier, oldTags, newTags) -}