diff --git a/.changelog/35674.txt b/.changelog/35674.txt new file mode 100644 index 00000000000..586f7a26d97 --- /dev/null +++ b/.changelog/35674.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_imagebuilder_lifecycle_policy +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 2567822f33f..73963937a1c 100644 --- a/go.mod +++ b/go.mod @@ -133,6 +133,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/healthlake v1.28.2 github.com/aws/aws-sdk-go-v2/service/iam v1.37.2 github.com/aws/aws-sdk-go-v2/service/identitystore v1.27.2 + github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.37.3 github.com/aws/aws-sdk-go-v2/service/inspector v1.25.2 github.com/aws/aws-sdk-go-v2/service/inspector2 v1.32.2 github.com/aws/aws-sdk-go-v2/service/internetmonitor v1.19.2 diff --git a/go.sum b/go.sum index b6647edcf22..0eeea31613b 100644 --- a/go.sum +++ b/go.sum @@ -278,6 +278,8 @@ github.com/aws/aws-sdk-go-v2/service/iam v1.37.2 h1:E7vCDUFeDN8uOk8Nb2d4E1howWS1 github.com/aws/aws-sdk-go-v2/service/iam v1.37.2/go.mod h1:QzMecFrIFYJ1cyxjlUoIFRzYSDX19gdqYUd0Tyws2J8= github.com/aws/aws-sdk-go-v2/service/identitystore v1.27.2 h1:QSrf6HsounqUtlFAwArhVNHPt3WXmSm0pz7RtojjBdo= github.com/aws/aws-sdk-go-v2/service/identitystore v1.27.2/go.mod h1:PtkL4CXOQy84zudggyFtyJFXCGDRY8igg9Nfo9df1sU= +github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.37.3 h1:Pdh3M/+3nyVJIEqD7UrbiBrtUIcC1uTk/NIrI6M/d3I= +github.com/aws/aws-sdk-go-v2/service/imagebuilder v1.37.3/go.mod h1:hqe5ZWGAMgZC14I2GrXc+tUPpucDfGb94RB/7IOdQ/o= github.com/aws/aws-sdk-go-v2/service/inspector v1.25.2 h1:xiQ70pTvXs9PFYmewDgeICVxRpiQP+cWQZmunV5uYKk= github.com/aws/aws-sdk-go-v2/service/inspector v1.25.2/go.mod h1:sDcAla3dh7DO6AAdh+29e+rowLaIcw2fxuwNFCIlBuA= github.com/aws/aws-sdk-go-v2/service/inspector2 v1.32.2 h1:D0nDW7y3KLPGShqF7gaKFRswY8ekG8jsfN4r3CWqAjQ= diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index cfbc51180e2..472c508bc6f 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -121,6 +121,7 @@ import ( healthlake_sdkv2 "github.com/aws/aws-sdk-go-v2/service/healthlake" iam_sdkv2 "github.com/aws/aws-sdk-go-v2/service/iam" identitystore_sdkv2 "github.com/aws/aws-sdk-go-v2/service/identitystore" + imagebuilder_sdkv2 "github.com/aws/aws-sdk-go-v2/service/imagebuilder" inspector_sdkv2 "github.com/aws/aws-sdk-go-v2/service/inspector" inspector2_sdkv2 "github.com/aws/aws-sdk-go-v2/service/inspector2" internetmonitor_sdkv2 "github.com/aws/aws-sdk-go-v2/service/internetmonitor" @@ -246,7 +247,6 @@ import ( workspaces_sdkv2 "github.com/aws/aws-sdk-go-v2/service/workspaces" workspacesweb_sdkv2 "github.com/aws/aws-sdk-go-v2/service/workspacesweb" xray_sdkv2 "github.com/aws/aws-sdk-go-v2/service/xray" - imagebuilder_sdkv1 "github.com/aws/aws-sdk-go/service/imagebuilder" simpledb_sdkv1 "github.com/aws/aws-sdk-go/service/simpledb" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/names" @@ -724,8 +724,8 @@ func (c *AWSClient) IdentityStoreClient(ctx context.Context) *identitystore_sdkv return errs.Must(client[*identitystore_sdkv2.Client](ctx, c, names.IdentityStore, make(map[string]any))) } -func (c *AWSClient) ImageBuilderConn(ctx context.Context) *imagebuilder_sdkv1.Imagebuilder { - return errs.Must(conn[*imagebuilder_sdkv1.Imagebuilder](ctx, c, names.ImageBuilder, make(map[string]any))) +func (c *AWSClient) ImageBuilderClient(ctx context.Context) *imagebuilder_sdkv2.Client { + return errs.Must(client[*imagebuilder_sdkv2.Client](ctx, c, names.ImageBuilder, make(map[string]any))) } func (c *AWSClient) InspectorClient(ctx context.Context) *inspector_sdkv2.Client { diff --git a/internal/conns/awsclient_resolveendpoint_gen.go b/internal/conns/awsclient_resolveendpoint_gen.go index 88db89bdb44..539381e2a34 100644 --- a/internal/conns/awsclient_resolveendpoint_gen.go +++ b/internal/conns/awsclient_resolveendpoint_gen.go @@ -21,22 +21,6 @@ func (c *AWSClient) resolveEndpoint(ctx context.Context, servicePackageName stri // Only SDK v1 packages. This is already supported by SDK v2. switch servicePackageName { - case "imagebuilder": - endpoint = aws.ToString(c.awsConfig.BaseEndpoint) - svc := os.Getenv("AWS_ENDPOINT_URL_IMAGEBUILDER") - if svc != "" { - return svc - } - - if base := os.Getenv("AWS_ENDPOINT_URL"); base != "" { - return base - } - - endpoint, found, err := resolveServiceBaseEndpoint(ctx, "imagebuilder", c.awsConfig.ConfigSources) - if found && err == nil { - return endpoint - } - case "simpledb": endpoint = aws.ToString(c.awsConfig.BaseEndpoint) svc := os.Getenv("AWS_ENDPOINT_URL_SIMPLEDB") diff --git a/internal/generate/namevaluesfilters/v1/file.tmpl b/internal/generate/namevaluesfilters/v1/file.tmpl deleted file mode 100644 index 7d1c17124d5..00000000000 --- a/internal/generate/namevaluesfilters/v1/file.tmpl +++ /dev/null @@ -1,38 +0,0 @@ -// Code generated by generators/servicefilters/main.go; DO NOT EDIT. - -package v1 - -import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports - "github.com/aws/aws-sdk-go/aws" -{{- range .SliceServiceNames }} -{{- if eq . (. | FilterPackage) }} - "github.com/aws/aws-sdk-go/service/{{ . }}" -{{- end }} -{{- end }} -) - -// []*SERVICE.Filter handling -{{- range .SliceServiceNames }} - -// {{ . | ProviderNameUpper }}Filters returns {{ . }} service filters. -func (filters NameValuesFilters) {{ . | ProviderNameUpper }}Filters() []*{{ . | FilterPackage }}.{{ . | FilterType }} { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*{{ . | FilterPackage }}.{{ . | FilterType }}, 0, len(m)) - - for k, v := range m { - filter := &{{ . | FilterPackage }}.{{ . | FilterType }}{ - {{ . | FilterTypeNameField }}: aws.String(k), - {{ . | FilterTypeValuesField }}: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} -{{- end }} diff --git a/internal/generate/namevaluesfilters/v1/main.go b/internal/generate/namevaluesfilters/v1/main.go deleted file mode 100644 index bf13a942439..00000000000 --- a/internal/generate/namevaluesfilters/v1/main.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go:build generate -// +build generate - -package main - -import ( - _ "embed" - "sort" - "text/template" - - "github.com/hashicorp/terraform-provider-aws/internal/generate/common" - namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" - "github.com/hashicorp/terraform-provider-aws/names" -) - -type TemplateData struct { - SliceServiceNames []string -} - -func main() { - const ( - filename = `service_filters_gen.go` - ) - g := common.NewGenerator() - - g.Infof("Generating internal/namevaluesfilters/v1/%s", filename) - - // Representing types such as []*fsx.Filter, []*rds.Filter, ... - sliceServiceNames := []string{ - "imagebuilder", - } - // Always sort to reduce any potential generation churn - sort.Strings(sliceServiceNames) - - td := TemplateData{ - SliceServiceNames: sliceServiceNames, - } - templateFuncMap := template.FuncMap{ - "FilterPackage": namevaluesfiltersv1.ServiceFilterPackage, - "FilterType": namevaluesfiltersv1.ServiceFilterType, - "FilterTypeNameField": namevaluesfiltersv1.ServiceFilterTypeNameField, - "FilterTypeValuesField": namevaluesfiltersv1.ServiceFilterTypeValuesField, - "ProviderNameUpper": names.ProviderNameUpper, - } - - d := g.NewGoFileDestination(filename) - - if err := d.WriteTemplate("namevaluesfilters", tmpl, td, templateFuncMap); err != nil { - g.Fatalf("generating file (%s): %s", filename, err) - } - - if err := d.Write(); err != nil { - g.Fatalf("generating file (%s): %s", filename, err) - } -} - -//go:embed file.tmpl -var tmpl string diff --git a/internal/generate/namevaluesfilters/v2/main.go b/internal/generate/namevaluesfilters/v2/main.go index 978a781fdcb..14445061f4d 100644 --- a/internal/generate/namevaluesfilters/v2/main.go +++ b/internal/generate/namevaluesfilters/v2/main.go @@ -30,6 +30,7 @@ func main() { // Representing types such as []*ec2.Filter, []*rds.Filter, ... sliceServiceNames := []string{ + "imagebuilder", "licensemanager", "rds", "secretsmanager", diff --git a/internal/namevaluesfilters/v1/README.md b/internal/namevaluesfilters/v1/README.md deleted file mode 100644 index 3793f5a824f..00000000000 --- a/internal/namevaluesfilters/v1/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# namevaluesfilters - -The `namevaluesfilters/v1` package is designed to provide a consistent interface for handling AWS resource filtering with AWS SDK for Go v1. - -This package implements a single `NameValuesFilters` type, which covers all filter handling logic, such as merging filters, via functions on the single type. The underlying implementation is compatible with Go operations such as `len()`. diff --git a/internal/namevaluesfilters/v1/generate.go b/internal/namevaluesfilters/v1/generate.go deleted file mode 100644 index e5639504819..00000000000 --- a/internal/namevaluesfilters/v1/generate.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go:generate go run ../../generate/namevaluesfilters/v1/main.go -// ONLY generate directives and package declaration! Do not add anything else to this file. - -package v1 diff --git a/internal/namevaluesfilters/v1/name_values_filters.go b/internal/namevaluesfilters/v1/name_values_filters.go deleted file mode 100644 index c8b81fabb46..00000000000 --- a/internal/namevaluesfilters/v1/name_values_filters.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v1 - -import ( - "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" -) - -type NameValuesFilters struct { - namevaluesfilters.NameValuesFilters -} - -func New(i interface{}) NameValuesFilters { - return NameValuesFilters{NameValuesFilters: namevaluesfilters.New(i)} -} diff --git a/internal/namevaluesfilters/v1/service_filters_gen.go b/internal/namevaluesfilters/v1/service_filters_gen.go deleted file mode 100644 index 545b6585356..00000000000 --- a/internal/namevaluesfilters/v1/service_filters_gen.go +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by generators/servicefilters/main.go; DO NOT EDIT. - -package v1 - -import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" -) - -// []*SERVICE.Filter handling - -// ImageBuilderFilters returns imagebuilder service filters. -func (filters NameValuesFilters) ImageBuilderFilters() []*imagebuilder.Filter { - m := filters.Map() - - if len(m) == 0 { - return nil - } - - result := make([]*imagebuilder.Filter, 0, len(m)) - - for k, v := range m { - filter := &imagebuilder.Filter{ - Name: aws.String(k), - Values: aws.StringSlice(v), - } - - result = append(result, filter) - } - - return result -} diff --git a/internal/namevaluesfilters/v1/service_generation_customizations.go b/internal/namevaluesfilters/v1/service_generation_customizations.go deleted file mode 100644 index 9af370ab50b..00000000000 --- a/internal/namevaluesfilters/v1/service_generation_customizations.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -// This file contains code generation customizations for each AWS Go SDK service. - -package v1 - -// ServiceFilterPackage determines the service filter type package. -func ServiceFilterPackage(serviceName string) string { - switch serviceName { - default: - return serviceName - } -} - -// ServiceFilterType determines the service filter type. -func ServiceFilterType(serviceName string) string { - switch serviceName { - case "resourcegroupstaggingapi": - return "TagFilter" - default: - return "Filter" - } -} - -// ServiceFilterTypeNameField determines the service filter type name field. -func ServiceFilterTypeNameField(serviceName string) string { - switch serviceName { - case "resourcegroupstaggingapi", "secretsmanager": - return "Key" - default: - return "Name" - } -} - -// ServiceFilterTypeValuesField determines the service filter type values field. -func ServiceFilterTypeValuesField(serviceName string) string { - switch serviceName { - default: - return "Values" - } -} diff --git a/internal/namevaluesfilters/v2/service_filters_gen.go b/internal/namevaluesfilters/v2/service_filters_gen.go index 0b9cc94b025..d86dbbe1023 100644 --- a/internal/namevaluesfilters/v2/service_filters_gen.go +++ b/internal/namevaluesfilters/v2/service_filters_gen.go @@ -4,6 +4,7 @@ package v2 import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports "github.com/aws/aws-sdk-go-v2/aws" + imagebuildertypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" licensemanagertypes "github.com/aws/aws-sdk-go-v2/service/licensemanager/types" rdstypes "github.com/aws/aws-sdk-go-v2/service/rds/types" route53resolvertypes "github.com/aws/aws-sdk-go-v2/service/route53resolver/types" @@ -12,6 +13,28 @@ import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports // []*SERVICE.Filter handling +// ImageBuilderFilters returns imagebuilder service filters. +func (filters NameValuesFilters) ImageBuilderFilters() []imagebuildertypes.Filter { + m := filters.Map() + + if len(m) == 0 { + return nil + } + + result := make([]imagebuildertypes.Filter, 0, len(m)) + + for k, v := range m { + filter := imagebuildertypes.Filter{ + Name: aws.String(k), + Values: v, + } + + result = append(result, filter) + } + + return result +} + // LicenseManagerFilters returns licensemanager service filters. func (filters NameValuesFilters) LicenseManagerFilters() []licensemanagertypes.Filter { m := filters.Map() diff --git a/internal/service/imagebuilder/component.go b/internal/service/imagebuilder/component.go index 43a18c59104..a392e27c6b4 100644 --- a/internal/service/imagebuilder/component.go +++ b/internal/service/imagebuilder/component.go @@ -7,29 +7,34 @@ import ( "context" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_imagebuilder_component", name="Component") // @Tags(identifierAttribute="id") -func ResourceComponent() *schema.Resource { +func resourceComponent() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceComponentCreate, ReadWithoutTimeout: resourceComponentRead, UpdateWithoutTimeout: resourceComponentUpdate, DeleteWithoutTimeout: resourceComponentDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -84,10 +89,10 @@ func ResourceComponent() *schema.Resource { Computed: true, }, "platform": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(imagebuilder.Platform_Values(), false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.Platform](), }, names.AttrSkipDestroy: { Type: schema.TypeBool, @@ -131,7 +136,7 @@ func ResourceComponent() *schema.Resource { func resourceComponentCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.CreateComponentInput{ ClientToken: aws.String(id.UniqueId()), @@ -159,11 +164,11 @@ func resourceComponentCreate(ctx context.Context, d *schema.ResourceData, meta i } if v, ok := d.GetOk("platform"); ok { - input.Platform = aws.String(v.(string)) + input.Platform = awstypes.Platform(v.(string)) } if v, ok := d.GetOk("supported_os_versions"); ok && v.(*schema.Set).Len() > 0 { - input.SupportedOsVersions = flex.ExpandStringSet(v.(*schema.Set)) + input.SupportedOsVersions = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk(names.AttrURI); ok { @@ -174,47 +179,33 @@ func resourceComponentCreate(ctx context.Context, d *schema.ResourceData, meta i input.SemanticVersion = aws.String(v.(string)) } - output, err := conn.CreateComponentWithContext(ctx, input) + output, err := conn.CreateComponent(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Image Builder Component: %s", err) } - if output == nil { - return sdkdiag.AppendErrorf(diags, "creating Image Builder Component: empty result") - } - - d.SetId(aws.StringValue(output.ComponentBuildVersionArn)) + d.SetId(aws.ToString(output.ComponentBuildVersionArn)) return append(diags, resourceComponentRead(ctx, d, meta)...) } func resourceComponentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetComponentInput{ - ComponentBuildVersionArn: aws.String(d.Id()), - } + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - output, err := conn.GetComponentWithContext(ctx, input) + component, err := findComponentByARN(ctx, conn, d.Id()) - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Image Builder Component (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Component (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Component (%s): %s", d.Id(), err) } - if output == nil || output.Component == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Component (%s): empty result", d.Id()) - } - - component := output.Component - d.Set(names.AttrARN, component.Arn) d.Set("change_description", component.ChangeDescription) d.Set("data", component.Data) @@ -225,13 +216,12 @@ func resourceComponentRead(ctx context.Context, d *schema.ResourceData, meta int d.Set(names.AttrName, component.Name) d.Set(names.AttrOwner, component.Owner) d.Set("platform", component.Platform) - d.Set("supported_os_versions", aws.StringValueSlice(component.SupportedOsVersions)) - - setTagsOut(ctx, component.Tags) - + d.Set("supported_os_versions", component.SupportedOsVersions) d.Set(names.AttrType, component.Type) d.Set(names.AttrVersion, component.Version) + setTagsOut(ctx, component.Tags) + return diags } @@ -245,21 +235,19 @@ func resourceComponentUpdate(ctx context.Context, d *schema.ResourceData, meta i func resourceComponentDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) if v, ok := d.GetOk(names.AttrSkipDestroy); ok && v.(bool) { - log.Printf("[DEBUG] Retaining Imagebuilder Component version %q", d.Id()) + log.Printf("[DEBUG] Retaining Image Builder Component version %q", d.Id()) return diags } - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.DeleteComponentInput{ + log.Printf("[DEBUG] Deleting Image Builder Component: %s", d.Id()) + _, err := conn.DeleteComponent(ctx, &imagebuilder.DeleteComponentInput{ ComponentBuildVersionArn: aws.String(d.Id()), - } + }) - _, err := conn.DeleteComponentWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { return diags } @@ -269,3 +257,28 @@ func resourceComponentDelete(ctx context.Context, d *schema.ResourceData, meta i return diags } + +func findComponentByARN(ctx context.Context, conn *imagebuilder.Client, arn string) (*awstypes.Component, error) { + input := &imagebuilder.GetComponentInput{ + ComponentBuildVersionArn: aws.String(arn), + } + + output, err := conn.GetComponent(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Component == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.Component, nil +} diff --git a/internal/service/imagebuilder/component_data_source.go b/internal/service/imagebuilder/component_data_source.go index 5915fa3edee..54a08e2d69e 100644 --- a/internal/service/imagebuilder/component_data_source.go +++ b/internal/service/imagebuilder/component_data_source.go @@ -6,8 +6,7 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -17,8 +16,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_component") -func DataSourceComponent() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_component", name="Component") +// @Tags +func dataSourceComponent() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceComponentRead, @@ -84,30 +84,18 @@ func DataSourceComponent() *schema.Resource { func dataSourceComponentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetComponentInput{} - - if v, ok := d.GetOk(names.AttrARN); ok { - input.ComponentBuildVersionArn = aws.String(v.(string)) - } - - output, err := conn.GetComponentWithContext(ctx, input) + arn := d.Get(names.AttrARN).(string) + component, err := findComponentByARN(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Component: %s", err) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Component (%s): %s", arn, err) } - if output == nil || output.Component == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Component: empty result") - } - - component := output.Component - - d.SetId(aws.StringValue(component.Arn)) - - d.Set(names.AttrARN, component.Arn) + arn = aws.ToString(component.Arn) + d.SetId(arn) + d.Set(names.AttrARN, arn) d.Set("change_description", component.ChangeDescription) d.Set("data", component.Data) d.Set("date_created", component.DateCreated) @@ -117,14 +105,11 @@ func dataSourceComponentRead(ctx context.Context, d *schema.ResourceData, meta i d.Set(names.AttrName, component.Name) d.Set(names.AttrOwner, component.Owner) d.Set("platform", component.Platform) - d.Set("supported_os_versions", aws.StringValueSlice(component.SupportedOsVersions)) - - if err := d.Set(names.AttrTags, KeyValueTags(ctx, component.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return sdkdiag.AppendErrorf(diags, "setting tags: %s", err) - } - + d.Set("supported_os_versions", component.SupportedOsVersions) d.Set(names.AttrType, component.Type) d.Set(names.AttrVersion, component.Version) + setTagsOut(ctx, component.Tags) + return diags } diff --git a/internal/service/imagebuilder/component_data_source_test.go b/internal/service/imagebuilder/component_data_source_test.go index 0bd64d63364..689fb7dd597 100644 --- a/internal/service/imagebuilder/component_data_source_test.go +++ b/internal/service/imagebuilder/component_data_source_test.go @@ -23,7 +23,6 @@ func TestAccImageBuilderComponentDataSource_arn(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.ImageBuilderServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckComponentDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccComponentDataSourceConfig_buildVersionARN(rName), diff --git a/internal/service/imagebuilder/component_test.go b/internal/service/imagebuilder/component_test.go index 061d7223230..c6b641a7164 100644 --- a/internal/service/imagebuilder/component_test.go +++ b/internal/service/imagebuilder/component_test.go @@ -9,15 +9,14 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfimagebuilder "github.com/hashicorp/terraform-provider-aws/internal/service/imagebuilder" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -45,10 +44,10 @@ func TestAccImageBuilderComponent_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, names.AttrKMSKeyID, ""), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), acctest.CheckResourceAttrAccountID(resourceName, names.AttrOwner), - resource.TestCheckResourceAttr(resourceName, "platform", imagebuilder.PlatformLinux), + resource.TestCheckResourceAttr(resourceName, "platform", string(awstypes.PlatformLinux)), resource.TestCheckResourceAttr(resourceName, "supported_os_versions.#", acctest.Ct0), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), - resource.TestCheckResourceAttr(resourceName, names.AttrType, imagebuilder.ComponentTypeBuild), + resource.TestCheckResourceAttr(resourceName, names.AttrType, string(awstypes.ComponentTypeBuild)), resource.TestCheckResourceAttr(resourceName, names.AttrVersion, "1.0.0"), ), }, @@ -185,7 +184,7 @@ func TestAccImageBuilderComponent_Platform_windows(t *testing.T) { Config: testAccComponentConfig_platformWindows(rName), Check: resource.ComposeTestCheckFunc( testAccCheckComponentExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, "platform", imagebuilder.PlatformWindows), + resource.TestCheckResourceAttr(resourceName, "platform", string(awstypes.PlatformWindows)), ), }, { @@ -301,56 +300,42 @@ func TestAccImageBuilderComponent_uri(t *testing.T) { func testAccCheckComponentDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_imagebuilder_component" { continue } - input := &imagebuilder.GetComponentInput{ - ComponentBuildVersionArn: aws.String(rs.Primary.ID), - } - - output, err := conn.GetComponentWithContext(ctx, input) + _, err := tfimagebuilder.FindComponentByARN(ctx, conn, rs.Primary.ID) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfresource.NotFound(err) { continue } if err != nil { - return fmt.Errorf("error getting Image Builder Component (%s): %w", rs.Primary.ID, err) + return err } - if output != nil { - return fmt.Errorf("Image Builder Component (%s) still exists", rs.Primary.ID) - } + return fmt.Errorf("Image Builder Component %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckComponentExists(ctx context.Context, resourceName string) resource.TestCheckFunc { +func testAccCheckComponentExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("resource not found: %s", resourceName) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetComponentInput{ - ComponentBuildVersionArn: aws.String(rs.Primary.ID), + return fmt.Errorf("Not found: %s", n) } - _, err := conn.GetComponentWithContext(ctx, input) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) - if err != nil { - return fmt.Errorf("error getting Image Builder Component (%s): %w", rs.Primary.ID, err) - } + _, err := tfimagebuilder.FindComponentByARN(ctx, conn, rs.Primary.ID) - return nil + return err } } diff --git a/internal/service/imagebuilder/components_data_source.go b/internal/service/imagebuilder/components_data_source.go index 0ec523328c9..2b073d63de6 100644 --- a/internal/service/imagebuilder/components_data_source.go +++ b/internal/service/imagebuilder/components_data_source.go @@ -6,22 +6,25 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" - namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKDataSource("aws_imagebuilder_components", name="Components") -func DataSourceComponents() *schema.Resource { +func dataSourceComponents() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceComponentsRead, + Schema: map[string]*schema.Schema{ names.AttrARNs: { Type: schema.TypeSet, @@ -35,9 +38,9 @@ func DataSourceComponents() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, names.AttrOwner: { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(imagebuilder.Ownership_Values(), false), + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[awstypes.Ownership](), }, }, } @@ -45,50 +48,48 @@ func DataSourceComponents() *schema.Resource { func dataSourceComponentsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.ListComponentsInput{} - if v, ok := d.GetOk(names.AttrOwner); ok { - input.Owner = aws.String(v.(string)) + if v, ok := d.GetOk(names.AttrFilter); ok { + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).ImageBuilderFilters() } - if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() + if v, ok := d.GetOk(names.AttrOwner); ok { + input.Owner = awstypes.Ownership(v.(string)) } - var results []*imagebuilder.ComponentVersion + components, err := findComponents(ctx, conn, input) - err := conn.ListComponentsPagesWithContext(ctx, input, func(page *imagebuilder.ListComponentsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Image Builder Components: %s", err) + } - for _, componentVersion := range page.ComponentVersionList { - if componentVersion == nil { - continue - } + d.SetId(meta.(*conns.AWSClient).Region) + d.Set(names.AttrARNs, tfslices.ApplyToAll(components, func(v awstypes.ComponentVersion) string { + return aws.ToString(v.Arn) + })) + d.Set(names.AttrNames, tfslices.ApplyToAll(components, func(v awstypes.ComponentVersion) string { + return aws.ToString(v.Name) + })) - results = append(results, componentVersion) - } + return diags +} - return !lastPage - }) +func findComponents(ctx context.Context, conn *imagebuilder.Client, input *imagebuilder.ListComponentsInput) ([]awstypes.ComponentVersion, error) { + var output []awstypes.ComponentVersion - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Components: %s", err) - } + pages := imagebuilder.NewListComponentsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - var arns, nms []string + if err != nil { + return nil, err + } - for _, r := range results { - arns = append(arns, aws.StringValue(r.Arn)) - nms = append(nms, aws.StringValue(r.Name)) + output = append(output, page.ComponentVersionList...) } - d.SetId(meta.(*conns.AWSClient).Region) - d.Set(names.AttrARNs, arns) - d.Set(names.AttrNames, nms) - - return diags + return output, nil } diff --git a/internal/service/imagebuilder/container_recipe.go b/internal/service/imagebuilder/container_recipe.go index 9141e30744b..ff0205da6eb 100644 --- a/internal/service/imagebuilder/container_recipe.go +++ b/internal/service/imagebuilder/container_recipe.go @@ -8,32 +8,38 @@ import ( "log" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/sdkv2/types/nullable" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_imagebuilder_container_recipe", name="Container Recipe") // @Tags(identifierAttribute="id") -func ResourceContainerRecipe() *schema.Resource { +func resourceContainerRecipe() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceContainerRecipeCreate, ReadWithoutTimeout: resourceContainerRecipeRead, UpdateWithoutTimeout: resourceContainerRecipeUpdate, DeleteWithoutTimeout: resourceContainerRecipeDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, + Schema: map[string]*schema.Schema{ names.AttrARN: { Type: schema.TypeString, @@ -179,10 +185,10 @@ func ResourceContainerRecipe() *schema.Resource { ValidateFunc: validation.IntBetween(1, 16000), }, names.AttrVolumeType: { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(imagebuilder.EbsVolumeType_Values(), false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.EbsVolumeType](), }, }, }, @@ -193,6 +199,7 @@ func ResourceContainerRecipe() *schema.Resource { // this is not compatible with TypeString's zero value. Type: schema.TypeBool, Optional: true, + Computed: true, ForceNew: true, }, names.AttrVirtualName: { @@ -279,13 +286,14 @@ func ResourceContainerRecipe() *schema.Resource { ValidateFunc: validation.StringLenBetween(1, 1024), }, }, + CustomizeDiff: verify.SetTagsDiff, } } func resourceContainerRecipeCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.CreateContainerRecipeInput{ ClientToken: aws.String(id.UniqueId()), @@ -297,7 +305,7 @@ func resourceContainerRecipeCreate(ctx context.Context, d *schema.ResourceData, } if v, ok := d.GetOk("container_type"); ok { - input.ContainerType = aws.String(v.(string)) + input.ContainerType = awstypes.ContainerType(v.(string)) } if v, ok := d.GetOk(names.AttrDescription); ok { @@ -329,7 +337,7 @@ func resourceContainerRecipeCreate(ctx context.Context, d *schema.ResourceData, } if v, ok := d.GetOk("platform_override"); ok { - input.PlatformOverride = aws.String(v.(string)) + input.PlatformOverride = awstypes.Platform(v.(string)) } if v, ok := d.GetOk("target_repository"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { @@ -344,73 +352,62 @@ func resourceContainerRecipeCreate(ctx context.Context, d *schema.ResourceData, input.WorkingDirectory = aws.String(v.(string)) } - output, err := conn.CreateContainerRecipeWithContext(ctx, input) + output, err := conn.CreateContainerRecipe(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Image Builder Container Recipe: %s", err) } - if output == nil { - return sdkdiag.AppendErrorf(diags, "creating Image Builder Container Recipe: empty response") - } - - d.SetId(aws.StringValue(output.ContainerRecipeArn)) + d.SetId(aws.ToString(output.ContainerRecipeArn)) return append(diags, resourceContainerRecipeRead(ctx, d, meta)...) } func resourceContainerRecipeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetContainerRecipeInput{ - ContainerRecipeArn: aws.String(d.Id()), - } + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - output, err := conn.GetContainerRecipeWithContext(ctx, input) + containerRecipe, err := findContainerRecipeByARN(ctx, conn, d.Id()) - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Image Builder Container Recipe (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Container Recipe (%s): %s", d.Id(), err) - } - - if output == nil || output.ContainerRecipe == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Container Recipe (%s): empty response", d.Id()) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Container Recipe (%s): %s", d.Id(), err) } - containerRecipe := output.ContainerRecipe - d.Set(names.AttrARN, containerRecipe.Arn) - d.Set("component", flattenComponentConfigurations(containerRecipe.Components)) + if err := d.Set("component", flattenComponentConfigurations(containerRecipe.Components)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting component: %s", err) + } d.Set("container_type", containerRecipe.ContainerType) d.Set("date_created", containerRecipe.DateCreated) d.Set(names.AttrDescription, containerRecipe.Description) d.Set("dockerfile_template_data", containerRecipe.DockerfileTemplateData) d.Set(names.AttrEncrypted, containerRecipe.Encrypted) - if containerRecipe.InstanceConfiguration != nil { - d.Set("instance_configuration", []interface{}{flattenInstanceConfiguration(containerRecipe.InstanceConfiguration)}) + if err := d.Set("instance_configuration", []interface{}{flattenInstanceConfiguration(containerRecipe.InstanceConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting instance_configuration: %s", err) + } } else { d.Set("instance_configuration", nil) } - d.Set(names.AttrKMSKeyID, containerRecipe.KmsKeyId) d.Set(names.AttrName, containerRecipe.Name) d.Set(names.AttrOwner, containerRecipe.Owner) d.Set("parent_image", containerRecipe.ParentImage) d.Set("platform", containerRecipe.Platform) - - setTagsOut(ctx, containerRecipe.Tags) - - d.Set("target_repository", []interface{}{flattenTargetContainerRepository(containerRecipe.TargetRepository)}) + if err := d.Set("target_repository", []interface{}{flattenTargetContainerRepository(containerRecipe.TargetRepository)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting target_repository: %s", err) + } d.Set(names.AttrVersion, containerRecipe.Version) d.Set("working_directory", containerRecipe.WorkingDirectory) + setTagsOut(ctx, containerRecipe.Tags) + return diags } @@ -424,15 +421,14 @@ func resourceContainerRecipeUpdate(ctx context.Context, d *schema.ResourceData, func resourceContainerRecipeDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.DeleteContainerRecipeInput{ + log.Printf("[DEBUG] Deleting Image Builder Container Recipe: %s", d.Id()) + _, err := conn.DeleteContainerRecipe(ctx, &imagebuilder.DeleteContainerRecipeInput{ ContainerRecipeArn: aws.String(d.Id()), - } + }) - _, err := conn.DeleteContainerRecipeWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { return diags } @@ -443,12 +439,37 @@ func resourceContainerRecipeDelete(ctx context.Context, d *schema.ResourceData, return diags } -func expandInstanceConfiguration(tfMap map[string]interface{}) *imagebuilder.InstanceConfiguration { +func findContainerRecipeByARN(ctx context.Context, conn *imagebuilder.Client, arn string) (*awstypes.ContainerRecipe, error) { + input := &imagebuilder.GetContainerRecipeInput{ + ContainerRecipeArn: aws.String(arn), + } + + output, err := conn.GetContainerRecipe(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.ContainerRecipe == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.ContainerRecipe, nil +} + +func expandInstanceConfiguration(tfMap map[string]interface{}) *awstypes.InstanceConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.InstanceConfiguration{} + apiObject := &awstypes.InstanceConfiguration{} if v, ok := tfMap["block_device_mapping"].(*schema.Set); ok && v.Len() > 0 { apiObject.BlockDeviceMappings = expandInstanceBlockDeviceMappings(v.List()) @@ -461,7 +482,7 @@ func expandInstanceConfiguration(tfMap map[string]interface{}) *imagebuilder.Ins return apiObject } -func flattenInstanceConfiguration(apiObject *imagebuilder.InstanceConfiguration) map[string]interface{} { +func flattenInstanceConfiguration(apiObject *awstypes.InstanceConfiguration) map[string]interface{} { if apiObject == nil { return nil } @@ -473,7 +494,7 @@ func flattenInstanceConfiguration(apiObject *imagebuilder.InstanceConfiguration) } if v := apiObject.Image; v != nil { - tfMap["image"] = aws.StringValue(v) + tfMap["image"] = aws.ToString(v) } return tfMap diff --git a/internal/service/imagebuilder/container_recipe_data_source.go b/internal/service/imagebuilder/container_recipe_data_source.go index e6efb63bfdb..e92c1756b2a 100644 --- a/internal/service/imagebuilder/container_recipe_data_source.go +++ b/internal/service/imagebuilder/container_recipe_data_source.go @@ -6,8 +6,7 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -17,10 +16,12 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_container_recipe") -func DataSourceContainerRecipe() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_container_recipe", name="Container Recipe") +// @Tags +func dataSourceContainerRecipe() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceContainerRecipeRead, + Schema: map[string]*schema.Schema{ names.AttrARN: { Type: schema.TypeString, @@ -167,7 +168,7 @@ func DataSourceContainerRecipe() *schema.Resource { Type: schema.TypeString, Computed: true, }, - names.AttrTags: tftags.TagsSchema(), + names.AttrTags: tftags.TagsSchemaComputed(), "target_repository": { Type: schema.TypeList, Computed: true, @@ -198,51 +199,45 @@ func DataSourceContainerRecipe() *schema.Resource { func dataSourceContainerRecipeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - - input := &imagebuilder.GetContainerRecipeInput{} + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - if v, ok := d.GetOk(names.AttrARN); ok { - input.ContainerRecipeArn = aws.String(v.(string)) - } - - output, err := conn.GetContainerRecipeWithContext(ctx, input) + arn := d.Get(names.AttrARN).(string) + containerRecipe, err := findContainerRecipeByARN(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Container Recipe (%s): %s", aws.StringValue(input.ContainerRecipeArn), err) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Container Recipe (%s): %s", arn, err) } - if output == nil || output.ContainerRecipe == nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Container Recipe (%s): empty response", aws.StringValue(input.ContainerRecipeArn)) + arn = aws.ToString(containerRecipe.Arn) + d.SetId(arn) + d.Set(names.AttrARN, arn) + if err := d.Set("component", flattenComponentConfigurations(containerRecipe.Components)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting component: %s", err) } - - containerRecipe := output.ContainerRecipe - - d.SetId(aws.StringValue(containerRecipe.Arn)) - d.Set(names.AttrARN, containerRecipe.Arn) - d.Set("component", flattenComponentConfigurations(containerRecipe.Components)) d.Set("container_type", containerRecipe.ContainerType) d.Set("date_created", containerRecipe.DateCreated) d.Set(names.AttrDescription, containerRecipe.Description) d.Set("dockerfile_template_data", containerRecipe.DockerfileTemplateData) d.Set(names.AttrEncrypted, containerRecipe.Encrypted) - if containerRecipe.InstanceConfiguration != nil { - d.Set("instance_configuration", []interface{}{flattenInstanceConfiguration(containerRecipe.InstanceConfiguration)}) + if err := d.Set("instance_configuration", []interface{}{flattenInstanceConfiguration(containerRecipe.InstanceConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting instance_configuration: %s", err) + } } else { d.Set("instance_configuration", nil) } - d.Set(names.AttrKMSKeyID, containerRecipe.KmsKeyId) d.Set(names.AttrName, containerRecipe.Name) d.Set(names.AttrOwner, containerRecipe.Owner) d.Set("parent_image", containerRecipe.ParentImage) d.Set("platform", containerRecipe.Platform) - d.Set(names.AttrTags, KeyValueTags(ctx, containerRecipe.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()) - d.Set("target_repository", []interface{}{flattenTargetContainerRepository(containerRecipe.TargetRepository)}) + if err := d.Set("target_repository", []interface{}{flattenTargetContainerRepository(containerRecipe.TargetRepository)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting target_repository: %s", err) + } d.Set(names.AttrVersion, containerRecipe.Version) d.Set("working_directory", containerRecipe.WorkingDirectory) + setTagsOut(ctx, containerRecipe.Tags) + return diags } diff --git a/internal/service/imagebuilder/container_recipe_test.go b/internal/service/imagebuilder/container_recipe_test.go index 6161aec600f..9a4f3d65bfa 100644 --- a/internal/service/imagebuilder/container_recipe_test.go +++ b/internal/service/imagebuilder/container_recipe_test.go @@ -9,15 +9,14 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfimagebuilder "github.com/hashicorp/terraform-provider-aws/internal/service/imagebuilder" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -50,7 +49,7 @@ func TestAccImageBuilderContainerRecipe_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), acctest.CheckResourceAttrAccountID(resourceName, names.AttrOwner), acctest.CheckResourceAttrRegionalARNAccountID(resourceName, "parent_image", "imagebuilder", "aws", "image/amazon-linux-x86-2/x.x.x"), - resource.TestCheckResourceAttr(resourceName, "platform", imagebuilder.PlatformLinux), + resource.TestCheckResourceAttr(resourceName, "platform", string(awstypes.PlatformLinux)), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttr(resourceName, "target_repository.#", acctest.Ct1), resource.TestCheckResourceAttrPair(resourceName, "target_repository.0.repository_name", "aws_ecr_repository.test", names.AttrName), @@ -466,7 +465,7 @@ func TestAccImageBuilderContainerRecipe_InstanceConfiguration_BlockDeviceMapping resource.TestCheckResourceAttr(resourceName, "instance_configuration.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.block_device_mapping.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.block_device_mapping.0.ebs.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.block_device_mapping.0.ebs.0.volume_type", imagebuilder.EbsVolumeTypeGp2), + resource.TestCheckResourceAttr(resourceName, "instance_configuration.0.block_device_mapping.0.ebs.0.volume_type", string(awstypes.EbsVolumeTypeGp2)), ), }, { @@ -696,56 +695,42 @@ func TestAccImageBuilderContainerRecipe_platformOverride(t *testing.T) { func testAccCheckContainerRecipeDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_imagebuilder_container_recipe" { continue } - input := &imagebuilder.GetContainerRecipeInput{ - ContainerRecipeArn: aws.String(rs.Primary.ID), - } - - output, err := conn.GetContainerRecipeWithContext(ctx, input) + _, err := tfimagebuilder.FindContainerRecipeByARN(ctx, conn, rs.Primary.ID) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfresource.NotFound(err) { continue } if err != nil { - return fmt.Errorf("error getting Image Builder Container Recipe (%s): %w", rs.Primary.ID, err) + return err } - if output != nil { - return fmt.Errorf("Image Builder Container Recipe (%s) still exists", rs.Primary.ID) - } + return fmt.Errorf("Image Builder Container Recipe %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckContainerRecipeExists(ctx context.Context, resourceName string) resource.TestCheckFunc { +func testAccCheckContainerRecipeExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("resource not found: %s", resourceName) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetContainerRecipeInput{ - ContainerRecipeArn: aws.String(rs.Primary.ID), + return fmt.Errorf("Not found: %s", n) } - _, err := conn.GetContainerRecipeWithContext(ctx, input) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) - if err != nil { - return fmt.Errorf("error getting Image Builder Container Recipe (%s): %w", rs.Primary.ID, err) - } + _, err := tfimagebuilder.FindContainerRecipeByARN(ctx, conn, rs.Primary.ID) - return nil + return err } } @@ -1385,7 +1370,7 @@ data "aws_ami" "test" { filter { name = "name" - values = ["amzn-ami-hvm-*-x86_64-gp2"] + values = ["amzn2-ami-hvm-*-x86_64-gp2"] } } diff --git a/internal/service/imagebuilder/container_recipes_data_source.go b/internal/service/imagebuilder/container_recipes_data_source.go index af125a2578a..222958e55c7 100644 --- a/internal/service/imagebuilder/container_recipes_data_source.go +++ b/internal/service/imagebuilder/container_recipes_data_source.go @@ -6,22 +6,25 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" - namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKDataSource("aws_imagebuilder_container_recipes", name="Container Recipes") -func DataSourceContainerRecipes() *schema.Resource { +func dataSourceContainerRecipes() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceContainerRecipesRead, + Schema: map[string]*schema.Schema{ names.AttrARNs: { Type: schema.TypeSet, @@ -35,9 +38,9 @@ func DataSourceContainerRecipes() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, names.AttrOwner: { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(imagebuilder.Ownership_Values(), false), + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[awstypes.Ownership](), }, }, } @@ -45,50 +48,48 @@ func DataSourceContainerRecipes() *schema.Resource { func dataSourceContainerRecipesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.ListContainerRecipesInput{} - if v, ok := d.GetOk(names.AttrOwner); ok { - input.Owner = aws.String(v.(string)) + if v, ok := d.GetOk(names.AttrFilter); ok { + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).ImageBuilderFilters() } - if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() + if v, ok := d.GetOk(names.AttrOwner); ok { + input.Owner = awstypes.Ownership(v.(string)) } - var results []*imagebuilder.ContainerRecipeSummary + containerRecipes, err := findContainerRecipes(ctx, conn, input) - err := conn.ListContainerRecipesPagesWithContext(ctx, input, func(page *imagebuilder.ListContainerRecipesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Image Builder Container Recipes: %s", err) + } - for _, containerRecipeSummary := range page.ContainerRecipeSummaryList { - if containerRecipeSummary == nil { - continue - } + d.SetId(meta.(*conns.AWSClient).Region) + d.Set(names.AttrARNs, tfslices.ApplyToAll(containerRecipes, func(v awstypes.ContainerRecipeSummary) string { + return aws.ToString(v.Arn) + })) + d.Set(names.AttrNames, tfslices.ApplyToAll(containerRecipes, func(v awstypes.ContainerRecipeSummary) string { + return aws.ToString(v.Name) + })) - results = append(results, containerRecipeSummary) - } + return diags +} - return !lastPage - }) +func findContainerRecipes(ctx context.Context, conn *imagebuilder.Client, input *imagebuilder.ListContainerRecipesInput) ([]awstypes.ContainerRecipeSummary, error) { + var output []awstypes.ContainerRecipeSummary - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Container Recipes: %s", err) - } + pages := imagebuilder.NewListContainerRecipesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - var arns, nms []string + if err != nil { + return nil, err + } - for _, r := range results { - arns = append(arns, aws.StringValue(r.Arn)) - nms = append(nms, aws.StringValue(r.Name)) + output = append(output, page.ContainerRecipeSummaryList...) } - d.SetId(meta.(*conns.AWSClient).Region) - d.Set(names.AttrARNs, arns) - d.Set(names.AttrNames, nms) - - return diags + return output, nil } diff --git a/internal/service/imagebuilder/distribution_configuration.go b/internal/service/imagebuilder/distribution_configuration.go index bde95746a93..913ed40c853 100644 --- a/internal/service/imagebuilder/distribution_configuration.go +++ b/internal/service/imagebuilder/distribution_configuration.go @@ -8,29 +8,33 @@ import ( "log" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_imagebuilder_distribution_configuration", name="Distribution Configuration") // @Tags(identifierAttribute="id") -func ResourceDistributionConfiguration() *schema.Resource { +func resourceDistributionConfiguration() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceDistributionConfigurationCreate, ReadWithoutTimeout: resourceDistributionConfigurationRead, UpdateWithoutTimeout: resourceDistributionConfigurationUpdate, DeleteWithoutTimeout: resourceDistributionConfigurationDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -294,7 +298,7 @@ func ResourceDistributionConfiguration() *schema.Resource { func resourceDistributionConfigurationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.CreateDistributionConfigurationInput{ ClientToken: aws.String(id.UniqueId()), @@ -313,52 +317,40 @@ func resourceDistributionConfigurationCreate(ctx context.Context, d *schema.Reso input.Name = aws.String(v.(string)) } - output, err := conn.CreateDistributionConfigurationWithContext(ctx, input) + output, err := conn.CreateDistributionConfiguration(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Image Builder Distribution Configuration: %s", err) } - if output == nil { - return sdkdiag.AppendErrorf(diags, "creating Image Builder Distribution Configuration: empty response") - } - - d.SetId(aws.StringValue(output.DistributionConfigurationArn)) + d.SetId(aws.ToString(output.DistributionConfigurationArn)) return append(diags, resourceDistributionConfigurationRead(ctx, d, meta)...) } func resourceDistributionConfigurationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetDistributionConfigurationInput{ - DistributionConfigurationArn: aws.String(d.Id()), - } + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - output, err := conn.GetDistributionConfigurationWithContext(ctx, input) + distributionConfiguration, err := findDistributionConfigurationByARN(ctx, conn, d.Id()) - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Image Builder Distribution Configuration (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Distribution Configuration (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Distribution Configuration (%s): %s", d.Id(), err) } - if output == nil || output.DistributionConfiguration == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Distribution Configuration (%s): empty response", d.Id()) - } - - distributionConfiguration := output.DistributionConfiguration - d.Set(names.AttrARN, distributionConfiguration.Arn) d.Set("date_created", distributionConfiguration.DateCreated) d.Set("date_updated", distributionConfiguration.DateUpdated) d.Set(names.AttrDescription, distributionConfiguration.Description) - d.Set("distribution", flattenDistributions(distributionConfiguration.Distributions)) + if err := d.Set("distribution", flattenDistributions(distributionConfiguration.Distributions)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting distribution: %s", err) + } d.Set(names.AttrName, distributionConfiguration.Name) setTagsOut(ctx, distributionConfiguration.Tags) @@ -368,7 +360,7 @@ func resourceDistributionConfigurationRead(ctx context.Context, d *schema.Resour func resourceDistributionConfigurationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) if d.HasChanges(names.AttrDescription, "distribution") { input := &imagebuilder.UpdateDistributionConfigurationInput{ @@ -383,8 +375,7 @@ func resourceDistributionConfigurationUpdate(ctx context.Context, d *schema.Reso input.Distributions = expandDistributions(v.(*schema.Set).List()) } - log.Printf("[DEBUG] UpdateDistributionConfiguration: %#v", input) - _, err := conn.UpdateDistributionConfigurationWithContext(ctx, input) + _, err := conn.UpdateDistributionConfiguration(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating Image Builder Distribution Configuration (%s): %s", d.Id(), err) @@ -396,15 +387,14 @@ func resourceDistributionConfigurationUpdate(ctx context.Context, d *schema.Reso func resourceDistributionConfigurationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.DeleteDistributionConfigurationInput{ + log.Printf("[DEBUG] Deleting Image Builder Distribution Configuration: %s", d.Id()) + _, err := conn.DeleteDistributionConfiguration(ctx, &imagebuilder.DeleteDistributionConfigurationInput{ DistributionConfigurationArn: aws.String(d.Id()), - } - - _, err := conn.DeleteDistributionConfigurationWithContext(ctx, input) + }) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { return diags } @@ -415,15 +405,40 @@ func resourceDistributionConfigurationDelete(ctx context.Context, d *schema.Reso return diags } -func expandAMIDistributionConfiguration(tfMap map[string]interface{}) *imagebuilder.AmiDistributionConfiguration { +func findDistributionConfigurationByARN(ctx context.Context, conn *imagebuilder.Client, arn string) (*awstypes.DistributionConfiguration, error) { + input := &imagebuilder.GetDistributionConfigurationInput{ + DistributionConfigurationArn: aws.String(arn), + } + + output, err := conn.GetDistributionConfiguration(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.DistributionConfiguration == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.DistributionConfiguration, nil +} + +func expandAMIDistributionConfiguration(tfMap map[string]interface{}) *awstypes.AmiDistributionConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.AmiDistributionConfiguration{} + apiObject := &awstypes.AmiDistributionConfiguration{} if v, ok := tfMap["ami_tags"].(map[string]interface{}); ok && len(v) > 0 { - apiObject.AmiTags = flex.ExpandStringMap(v) + apiObject.AmiTags = flex.ExpandStringValueMap(v) } if v, ok := tfMap[names.AttrDescription].(string); ok && v != "" { @@ -443,21 +458,21 @@ func expandAMIDistributionConfiguration(tfMap map[string]interface{}) *imagebuil } if v, ok := tfMap["target_account_ids"].(*schema.Set); ok && v.Len() > 0 { - apiObject.TargetAccountIds = flex.ExpandStringSet(v) + apiObject.TargetAccountIds = flex.ExpandStringValueSet(v) } return apiObject } -func expandContainerDistributionConfiguration(tfMap map[string]interface{}) *imagebuilder.ContainerDistributionConfiguration { +func expandContainerDistributionConfiguration(tfMap map[string]interface{}) *awstypes.ContainerDistributionConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.ContainerDistributionConfiguration{} + apiObject := &awstypes.ContainerDistributionConfiguration{} if v, ok := tfMap["container_tags"].(*schema.Set); ok && v.Len() > 0 { - apiObject.ContainerTags = flex.ExpandStringSet(v) + apiObject.ContainerTags = flex.ExpandStringValueSet(v) } if v, ok := tfMap[names.AttrDescription].(string); ok && v != "" { @@ -471,12 +486,12 @@ func expandContainerDistributionConfiguration(tfMap map[string]interface{}) *ima return apiObject } -func expandLaunchTemplateConfigurations(tfList []interface{}) []*imagebuilder.LaunchTemplateConfiguration { +func expandLaunchTemplateConfigurations(tfList []interface{}) []awstypes.LaunchTemplateConfiguration { if len(tfList) == 0 { return nil } - var apiObjects []*imagebuilder.LaunchTemplateConfiguration + var apiObjects []awstypes.LaunchTemplateConfiguration for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) @@ -491,18 +506,18 @@ func expandLaunchTemplateConfigurations(tfList []interface{}) []*imagebuilder.La continue } - apiObjects = append(apiObjects, apiObject) + apiObjects = append(apiObjects, *apiObject) } return apiObjects } -func expandDistribution(tfMap map[string]interface{}) *imagebuilder.Distribution { +func expandDistribution(tfMap map[string]interface{}) *awstypes.Distribution { if tfMap == nil { return nil } - apiObject := &imagebuilder.Distribution{} + apiObject := &awstypes.Distribution{} if v, ok := tfMap["ami_distribution_configuration"].([]interface{}); ok && len(v) > 0 && v[0] != nil { apiObject.AmiDistributionConfiguration = expandAMIDistributionConfiguration(v[0].(map[string]interface{})) @@ -521,7 +536,7 @@ func expandDistribution(tfMap map[string]interface{}) *imagebuilder.Distribution } if v, ok := tfMap["license_configuration_arns"].(*schema.Set); ok && v.Len() > 0 { - apiObject.LicenseConfigurationArns = flex.ExpandStringSet(v) + apiObject.LicenseConfigurationArns = flex.ExpandStringValueSet(v) } if v, ok := tfMap[names.AttrRegion].(string); ok && v != "" { @@ -531,12 +546,12 @@ func expandDistribution(tfMap map[string]interface{}) *imagebuilder.Distribution return apiObject } -func expandDistributions(tfList []interface{}) []*imagebuilder.Distribution { +func expandDistributions(tfList []interface{}) []awstypes.Distribution { if len(tfList) == 0 { return nil } - var apiObjects []*imagebuilder.Distribution + var apiObjects []awstypes.Distribution for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) @@ -558,62 +573,62 @@ func expandDistributions(tfList []interface{}) []*imagebuilder.Distribution { continue } - apiObjects = append(apiObjects, apiObject) + apiObjects = append(apiObjects, *apiObject) } return apiObjects } -func expandLaunchPermissionConfiguration(tfMap map[string]interface{}) *imagebuilder.LaunchPermissionConfiguration { +func expandLaunchPermissionConfiguration(tfMap map[string]interface{}) *awstypes.LaunchPermissionConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.LaunchPermissionConfiguration{} + apiObject := &awstypes.LaunchPermissionConfiguration{} if v, ok := tfMap["organization_arns"].(*schema.Set); ok && v.Len() > 0 { - apiObject.OrganizationArns = flex.ExpandStringSet(v) + apiObject.OrganizationArns = flex.ExpandStringValueSet(v) } if v, ok := tfMap["organizational_unit_arns"].(*schema.Set); ok && v.Len() > 0 { - apiObject.OrganizationalUnitArns = flex.ExpandStringSet(v) + apiObject.OrganizationalUnitArns = flex.ExpandStringValueSet(v) } if v, ok := tfMap["user_ids"].(*schema.Set); ok && v.Len() > 0 { - apiObject.UserIds = flex.ExpandStringSet(v) + apiObject.UserIds = flex.ExpandStringValueSet(v) } if v, ok := tfMap["user_groups"].(*schema.Set); ok && v.Len() > 0 { - apiObject.UserGroups = flex.ExpandStringSet(v) + apiObject.UserGroups = flex.ExpandStringValueSet(v) } return apiObject } -func expandTargetContainerRepository(tfMap map[string]interface{}) *imagebuilder.TargetContainerRepository { +func expandTargetContainerRepository(tfMap map[string]interface{}) *awstypes.TargetContainerRepository { if tfMap == nil { return nil } - apiObject := &imagebuilder.TargetContainerRepository{} + apiObject := &awstypes.TargetContainerRepository{} if v, ok := tfMap[names.AttrRepositoryName].(string); ok && v != "" { apiObject.RepositoryName = aws.String(v) } if v, ok := tfMap["service"].(string); ok && v != "" { - apiObject.Service = aws.String(v) + apiObject.Service = awstypes.ContainerRepositoryService(v) } return apiObject } -func expandFastLaunchConfigurations(tfList []interface{}) []*imagebuilder.FastLaunchConfiguration { +func expandFastLaunchConfigurations(tfList []interface{}) []awstypes.FastLaunchConfiguration { if len(tfList) == 0 { return nil } - var apiObjects []*imagebuilder.FastLaunchConfiguration + var apiObjects []awstypes.FastLaunchConfiguration for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) @@ -628,25 +643,25 @@ func expandFastLaunchConfigurations(tfList []interface{}) []*imagebuilder.FastLa continue } - apiObjects = append(apiObjects, apiObject) + apiObjects = append(apiObjects, *apiObject) } return apiObjects } -func expandFastLaunchConfiguration(tfMap map[string]interface{}) *imagebuilder.FastLaunchConfiguration { +func expandFastLaunchConfiguration(tfMap map[string]interface{}) *awstypes.FastLaunchConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.FastLaunchConfiguration{} + apiObject := &awstypes.FastLaunchConfiguration{} if v, ok := tfMap[names.AttrAccountID].(string); ok && v != "" { apiObject.AccountId = aws.String(v) } if v, ok := tfMap[names.AttrEnabled].(bool); ok { - apiObject.Enabled = aws.Bool(v) + apiObject.Enabled = v } if v, ok := tfMap[names.AttrLaunchTemplate].([]interface{}); ok && len(v) > 0 && v[0] != nil { @@ -654,7 +669,7 @@ func expandFastLaunchConfiguration(tfMap map[string]interface{}) *imagebuilder.F } if v, ok := tfMap["max_parallel_launches"].(int); ok && v != 0 { - apiObject.MaxParallelLaunches = aws.Int64(int64(v)) + apiObject.MaxParallelLaunches = aws.Int32(int32(v)) } if v, ok := tfMap["snapshot_configuration"].([]interface{}); ok && len(v) > 0 && v[0] != nil { @@ -664,12 +679,12 @@ func expandFastLaunchConfiguration(tfMap map[string]interface{}) *imagebuilder.F return apiObject } -func expandFastLaunchLaunchTemplateSpecification(tfMap map[string]interface{}) *imagebuilder.FastLaunchLaunchTemplateSpecification { +func expandFastLaunchLaunchTemplateSpecification(tfMap map[string]interface{}) *awstypes.FastLaunchLaunchTemplateSpecification { if tfMap == nil { return nil } - apiObject := &imagebuilder.FastLaunchLaunchTemplateSpecification{} + apiObject := &awstypes.FastLaunchLaunchTemplateSpecification{} if v, ok := tfMap["launch_template_id"].(string); ok && v != "" { apiObject.LaunchTemplateId = aws.String(v) @@ -686,26 +701,26 @@ func expandFastLaunchLaunchTemplateSpecification(tfMap map[string]interface{}) * return apiObject } -func expandFastLaunchSnapshotConfiguration(tfMap map[string]interface{}) *imagebuilder.FastLaunchSnapshotConfiguration { +func expandFastLaunchSnapshotConfiguration(tfMap map[string]interface{}) *awstypes.FastLaunchSnapshotConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.FastLaunchSnapshotConfiguration{} + apiObject := &awstypes.FastLaunchSnapshotConfiguration{} if v, ok := tfMap["target_resource_count"].(int); ok && v != 0 { - apiObject.TargetResourceCount = aws.Int64(int64(v)) + apiObject.TargetResourceCount = aws.Int32(int32(v)) } return apiObject } -func expandLaunchTemplateConfiguration(tfMap map[string]interface{}) *imagebuilder.LaunchTemplateConfiguration { +func expandLaunchTemplateConfiguration(tfMap map[string]interface{}) *awstypes.LaunchTemplateConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.LaunchTemplateConfiguration{} + apiObject := &awstypes.LaunchTemplateConfiguration{} if v, ok := tfMap["launch_template_id"].(string); ok && v != "" { apiObject.LaunchTemplateId = aws.String(v) @@ -722,7 +737,7 @@ func expandLaunchTemplateConfiguration(tfMap map[string]interface{}) *imagebuild return apiObject } -func flattenAMIDistributionConfiguration(apiObject *imagebuilder.AmiDistributionConfiguration) map[string]interface{} { +func flattenAMIDistributionConfiguration(apiObject *awstypes.AmiDistributionConfiguration) map[string]interface{} { if apiObject == nil { return nil } @@ -730,15 +745,15 @@ func flattenAMIDistributionConfiguration(apiObject *imagebuilder.AmiDistribution tfMap := map[string]interface{}{} if v := apiObject.AmiTags; v != nil { - tfMap["ami_tags"] = aws.StringValueMap(v) + tfMap["ami_tags"] = aws.StringMap(v) } if v := apiObject.Description; v != nil { - tfMap[names.AttrDescription] = aws.StringValue(v) + tfMap[names.AttrDescription] = aws.ToString(v) } if v := apiObject.KmsKeyId; v != nil { - tfMap[names.AttrKMSKeyID] = aws.StringValue(v) + tfMap[names.AttrKMSKeyID] = aws.ToString(v) } if v := apiObject.LaunchPermission; v != nil { @@ -746,17 +761,17 @@ func flattenAMIDistributionConfiguration(apiObject *imagebuilder.AmiDistribution } if v := apiObject.Name; v != nil { - tfMap[names.AttrName] = aws.StringValue(v) + tfMap[names.AttrName] = aws.ToString(v) } if v := apiObject.TargetAccountIds; v != nil { - tfMap["target_account_ids"] = aws.StringValueSlice(v) + tfMap["target_account_ids"] = aws.StringSlice(v) } return tfMap } -func flattenContainerDistributionConfiguration(apiObject *imagebuilder.ContainerDistributionConfiguration) map[string]interface{} { +func flattenContainerDistributionConfiguration(apiObject *awstypes.ContainerDistributionConfiguration) map[string]interface{} { if apiObject == nil { return nil } @@ -764,11 +779,11 @@ func flattenContainerDistributionConfiguration(apiObject *imagebuilder.Container tfMap := map[string]interface{}{} if v := apiObject.ContainerTags; v != nil { - tfMap["container_tags"] = aws.StringValueSlice(v) + tfMap["container_tags"] = aws.StringSlice(v) } if v := apiObject.Description; v != nil { - tfMap[names.AttrDescription] = aws.StringValue(v) + tfMap[names.AttrDescription] = aws.ToString(v) } if v := apiObject.TargetRepository; v != nil { @@ -778,7 +793,7 @@ func flattenContainerDistributionConfiguration(apiObject *imagebuilder.Container return tfMap } -func flattenLaunchTemplateConfigurations(apiObjects []*imagebuilder.LaunchTemplateConfiguration) []interface{} { +func flattenLaunchTemplateConfigurations(apiObjects []awstypes.LaunchTemplateConfiguration) []interface{} { if apiObjects == nil { return nil } @@ -786,21 +801,13 @@ func flattenLaunchTemplateConfigurations(apiObjects []*imagebuilder.LaunchTempla var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - tfList = append(tfList, flattenLaunchTemplateConfiguration(apiObject)) } return tfList } -func flattenDistribution(apiObject *imagebuilder.Distribution) map[string]interface{} { - if apiObject == nil { - return nil - } - +func flattenDistribution(apiObject awstypes.Distribution) map[string]interface{} { tfMap := map[string]interface{}{} if v := apiObject.AmiDistributionConfiguration; v != nil { @@ -820,17 +827,17 @@ func flattenDistribution(apiObject *imagebuilder.Distribution) map[string]interf } if v := apiObject.LicenseConfigurationArns; v != nil { - tfMap["license_configuration_arns"] = aws.StringValueSlice(v) + tfMap["license_configuration_arns"] = aws.StringSlice(v) } if v := apiObject.Region; v != nil { - tfMap[names.AttrRegion] = aws.StringValue(v) + tfMap[names.AttrRegion] = aws.ToString(v) } return tfMap } -func flattenDistributions(apiObjects []*imagebuilder.Distribution) []interface{} { +func flattenDistributions(apiObjects []awstypes.Distribution) []interface{} { if len(apiObjects) == 0 { return nil } @@ -838,17 +845,13 @@ func flattenDistributions(apiObjects []*imagebuilder.Distribution) []interface{} var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - tfList = append(tfList, flattenDistribution(apiObject)) } return tfList } -func flattenLaunchPermissionConfiguration(apiObject *imagebuilder.LaunchPermissionConfiguration) map[string]interface{} { +func flattenLaunchPermissionConfiguration(apiObject *awstypes.LaunchPermissionConfiguration) map[string]interface{} { if apiObject == nil { return nil } @@ -856,25 +859,25 @@ func flattenLaunchPermissionConfiguration(apiObject *imagebuilder.LaunchPermissi tfMap := map[string]interface{}{} if v := apiObject.OrganizationArns; v != nil { - tfMap["organization_arns"] = aws.StringValueSlice(v) + tfMap["organization_arns"] = aws.StringSlice(v) } if v := apiObject.OrganizationalUnitArns; v != nil { - tfMap["organizational_unit_arns"] = aws.StringValueSlice(v) + tfMap["organizational_unit_arns"] = aws.StringSlice(v) } if v := apiObject.UserGroups; v != nil { - tfMap["user_groups"] = aws.StringValueSlice(v) + tfMap["user_groups"] = aws.StringSlice(v) } if v := apiObject.UserIds; v != nil { - tfMap["user_ids"] = aws.StringValueSlice(v) + tfMap["user_ids"] = aws.StringSlice(v) } return tfMap } -func flattenTargetContainerRepository(apiObject *imagebuilder.TargetContainerRepository) map[string]interface{} { +func flattenTargetContainerRepository(apiObject *awstypes.TargetContainerRepository) map[string]interface{} { if apiObject == nil { return nil } @@ -882,39 +885,31 @@ func flattenTargetContainerRepository(apiObject *imagebuilder.TargetContainerRep tfMap := map[string]interface{}{} if v := apiObject.RepositoryName; v != nil { - tfMap[names.AttrRepositoryName] = aws.StringValue(v) + tfMap[names.AttrRepositoryName] = aws.ToString(v) } - if v := apiObject.Service; v != nil { - tfMap["service"] = aws.StringValue(v) - } + tfMap["service"] = string(apiObject.Service) return tfMap } -func flattenLaunchTemplateConfiguration(apiObject *imagebuilder.LaunchTemplateConfiguration) map[string]interface{} { - if apiObject == nil { - return nil +func flattenLaunchTemplateConfiguration(apiObject awstypes.LaunchTemplateConfiguration) map[string]interface{} { + tfMap := map[string]interface{}{ + "default": apiObject.SetDefaultVersion, } - tfMap := map[string]interface{}{} - if v := apiObject.LaunchTemplateId; v != nil { - tfMap["launch_template_id"] = aws.StringValue(v) - } - - if v := apiObject.SetDefaultVersion; v != nil { - tfMap["default"] = aws.BoolValue(v) + tfMap["launch_template_id"] = aws.ToString(v) } if v := apiObject.AccountId; v != nil { - tfMap[names.AttrAccountID] = aws.StringValue(v) + tfMap[names.AttrAccountID] = aws.ToString(v) } return tfMap } -func flattenFastLaunchConfigurations(apiObjects []*imagebuilder.FastLaunchConfiguration) []interface{} { +func flattenFastLaunchConfigurations(apiObjects []awstypes.FastLaunchConfiguration) []interface{} { if apiObjects == nil { return nil } @@ -922,37 +917,27 @@ func flattenFastLaunchConfigurations(apiObjects []*imagebuilder.FastLaunchConfig var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - tfList = append(tfList, flattenFastLaunchConfiguration(apiObject)) } return tfList } -func flattenFastLaunchConfiguration(apiObject *imagebuilder.FastLaunchConfiguration) map[string]interface{} { - if apiObject == nil { - return nil - } - +func flattenFastLaunchConfiguration(apiObject awstypes.FastLaunchConfiguration) map[string]interface{} { tfMap := map[string]interface{}{} if v := apiObject.AccountId; v != nil { - tfMap[names.AttrAccountID] = aws.StringValue(v) + tfMap[names.AttrAccountID] = aws.ToString(v) } - if v := apiObject.Enabled; v != nil { - tfMap[names.AttrEnabled] = aws.BoolValue(v) - } + tfMap[names.AttrEnabled] = aws.Bool(apiObject.Enabled) if v := apiObject.LaunchTemplate; v != nil { tfMap[names.AttrLaunchTemplate] = []interface{}{flattenFastLaunchLaunchTemplateSpecification(v)} } if v := apiObject.MaxParallelLaunches; v != nil { - tfMap["max_parallel_launches"] = aws.Int64Value(v) + tfMap["max_parallel_launches"] = aws.ToInt32(v) } if v := apiObject.SnapshotConfiguration; v != nil { @@ -962,7 +947,7 @@ func flattenFastLaunchConfiguration(apiObject *imagebuilder.FastLaunchConfigurat return tfMap } -func flattenFastLaunchLaunchTemplateSpecification(apiObject *imagebuilder.FastLaunchLaunchTemplateSpecification) map[string]interface{} { +func flattenFastLaunchLaunchTemplateSpecification(apiObject *awstypes.FastLaunchLaunchTemplateSpecification) map[string]interface{} { if apiObject == nil { return nil } @@ -970,21 +955,21 @@ func flattenFastLaunchLaunchTemplateSpecification(apiObject *imagebuilder.FastLa tfMap := map[string]interface{}{} if v := apiObject.LaunchTemplateId; v != nil { - tfMap["launch_template_id"] = aws.StringValue(v) + tfMap["launch_template_id"] = aws.ToString(v) } if v := apiObject.LaunchTemplateName; v != nil { - tfMap["launch_template_name"] = aws.StringValue(v) + tfMap["launch_template_name"] = aws.ToString(v) } if v := apiObject.LaunchTemplateVersion; v != nil { - tfMap["launch_template_version"] = aws.StringValue(v) + tfMap["launch_template_version"] = aws.ToString(v) } return tfMap } -func flattenFastLaunchSnapshotConfiguration(apiObject *imagebuilder.FastLaunchSnapshotConfiguration) map[string]interface{} { +func flattenFastLaunchSnapshotConfiguration(apiObject *awstypes.FastLaunchSnapshotConfiguration) map[string]interface{} { if apiObject == nil { return nil } @@ -992,7 +977,7 @@ func flattenFastLaunchSnapshotConfiguration(apiObject *imagebuilder.FastLaunchSn tfMap := map[string]interface{}{} if v := apiObject.TargetResourceCount; v != nil { - tfMap["target_resource_count"] = aws.Int64Value(v) + tfMap["target_resource_count"] = aws.ToInt32(v) } return tfMap diff --git a/internal/service/imagebuilder/distribution_configuration_data_source.go b/internal/service/imagebuilder/distribution_configuration_data_source.go index d0090343426..f560352bcc5 100644 --- a/internal/service/imagebuilder/distribution_configuration_data_source.go +++ b/internal/service/imagebuilder/distribution_configuration_data_source.go @@ -6,8 +6,7 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -17,8 +16,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_distribution_configuration") -func DataSourceDistributionConfiguration() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_distribution_configuration", name="Distribution Configuration") +// @Tags +func dataSourceDistributionConfiguration() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceDistributionConfigurationRead, @@ -241,35 +241,27 @@ func DataSourceDistributionConfiguration() *schema.Resource { func dataSourceDistributionConfigurationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetDistributionConfigurationInput{} - - if v, ok := d.GetOk(names.AttrARN); ok { - input.DistributionConfigurationArn = aws.String(v.(string)) - } - - output, err := conn.GetDistributionConfigurationWithContext(ctx, input) + arn := d.Get(names.AttrARN).(string) + distributionConfiguration, err := findDistributionConfigurationByARN(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Distribution Configuration (%s): %s", d.Id(), err) - } - - if output == nil || output.DistributionConfiguration == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Distribution Configuration (%s): empty response", d.Id()) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Distribution Configuration (%s): %s", arn, err) } - distributionConfiguration := output.DistributionConfiguration - - d.SetId(aws.StringValue(distributionConfiguration.Arn)) - d.Set(names.AttrARN, distributionConfiguration.Arn) + arn = aws.ToString(distributionConfiguration.Arn) + d.SetId(arn) + d.Set(names.AttrARN, arn) d.Set("date_created", distributionConfiguration.DateCreated) d.Set("date_updated", distributionConfiguration.DateUpdated) d.Set(names.AttrDescription, distributionConfiguration.Description) - d.Set("distribution", flattenDistributions(distributionConfiguration.Distributions)) + if err := d.Set("distribution", flattenDistributions(distributionConfiguration.Distributions)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting distribution: %s", err) + } d.Set(names.AttrName, distributionConfiguration.Name) - d.Set(names.AttrTags, KeyValueTags(ctx, distributionConfiguration.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()) + + setTagsOut(ctx, distributionConfiguration.Tags) return diags } diff --git a/internal/service/imagebuilder/distribution_configuration_test.go b/internal/service/imagebuilder/distribution_configuration_test.go index 8d33b8f9ff2..212f8d4b62a 100644 --- a/internal/service/imagebuilder/distribution_configuration_test.go +++ b/internal/service/imagebuilder/distribution_configuration_test.go @@ -8,15 +8,13 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfimagebuilder "github.com/hashicorp/terraform-provider-aws/internal/service/imagebuilder" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -926,56 +924,42 @@ func TestAccImageBuilderDistributionConfiguration_tags(t *testing.T) { func testAccCheckDistributionConfigurationDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_imagebuilder_distribution_configuration" { continue } - input := &imagebuilder.GetDistributionConfigurationInput{ - DistributionConfigurationArn: aws.String(rs.Primary.ID), - } - - output, err := conn.GetDistributionConfigurationWithContext(ctx, input) + _, err := tfimagebuilder.FindDistributionConfigurationByARN(ctx, conn, rs.Primary.ID) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfresource.NotFound(err) { continue } if err != nil { - return fmt.Errorf("error getting Image Builder Distribution Configuration (%s): %w", rs.Primary.ID, err) + return err } - if output != nil { - return fmt.Errorf("Image Builder Distribution Configuration (%s) still exists", rs.Primary.ID) - } + return fmt.Errorf("Image Builder Distribution Configuration %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckDistributionConfigurationExists(ctx context.Context, resourceName string) resource.TestCheckFunc { +func testAccCheckDistributionConfigurationExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("resource not found: %s", resourceName) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetDistributionConfigurationInput{ - DistributionConfigurationArn: aws.String(rs.Primary.ID), + return fmt.Errorf("Not found: %s", n) } - _, err := conn.GetDistributionConfigurationWithContext(ctx, input) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) - if err != nil { - return fmt.Errorf("error getting Image Builder Distribution Configuration (%s): %w", rs.Primary.ID, err) - } + _, err := tfimagebuilder.FindDistributionConfigurationByARN(ctx, conn, rs.Primary.ID) - return nil + return err } } @@ -1331,7 +1315,7 @@ data "aws_caller_identity" "current" {} resource "aws_launch_template" "test" { instance_type = "t2.micro" - name = %[1]q + name = "%[1]s-1" } resource "aws_imagebuilder_distribution_configuration" "test" { @@ -1362,7 +1346,7 @@ data "aws_caller_identity" "current" {} resource "aws_launch_template" "test2" { instance_type = "t2.micro" - name = %[1]q + name = "%[1]s-2" } resource "aws_imagebuilder_distribution_configuration" "test" { diff --git a/internal/service/imagebuilder/distribution_configurations_data_source.go b/internal/service/imagebuilder/distribution_configurations_data_source.go index f36f054521b..5eb8ef15a4f 100644 --- a/internal/service/imagebuilder/distribution_configurations_data_source.go +++ b/internal/service/imagebuilder/distribution_configurations_data_source.go @@ -6,21 +6,24 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" - namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_distribution_configurations") -func DataSourceDistributionConfigurations() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_distribution_configurations", name="Distribution Configurations") +func dataSourceDistributionConfigurations() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceDistributionConfigurationsRead, + Schema: map[string]*schema.Schema{ names.AttrARNs: { Type: schema.TypeSet, @@ -39,46 +42,44 @@ func DataSourceDistributionConfigurations() *schema.Resource { func dataSourceDistributionConfigurationsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.ListDistributionConfigurationsInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).ImageBuilderFilters() } - var results []*imagebuilder.DistributionConfigurationSummary + distributionConfigurations, err := findDistributionConfigurations(ctx, conn, input) - err := conn.ListDistributionConfigurationsPagesWithContext(ctx, input, func(page *imagebuilder.ListDistributionConfigurationsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Image Builder Distribution Configurations: %s", err) + } - for _, distributionConfigurationSummary := range page.DistributionConfigurationSummaryList { - if distributionConfigurationSummary == nil { - continue - } + d.SetId(meta.(*conns.AWSClient).Region) + d.Set(names.AttrARNs, tfslices.ApplyToAll(distributionConfigurations, func(v awstypes.DistributionConfigurationSummary) string { + return aws.ToString(v.Arn) + })) + d.Set(names.AttrNames, tfslices.ApplyToAll(distributionConfigurations, func(v awstypes.DistributionConfigurationSummary) string { + return aws.ToString(v.Name) + })) - results = append(results, distributionConfigurationSummary) - } + return diags +} - return !lastPage - }) +func findDistributionConfigurations(ctx context.Context, conn *imagebuilder.Client, input *imagebuilder.ListDistributionConfigurationsInput) ([]awstypes.DistributionConfigurationSummary, error) { + var output []awstypes.DistributionConfigurationSummary - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Distribution Configurations: %s", err) - } + pages := imagebuilder.NewListDistributionConfigurationsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - var arns, nms []string + if err != nil { + return nil, err + } - for _, r := range results { - arns = append(arns, aws.StringValue(r.Arn)) - nms = append(nms, aws.StringValue(r.Name)) + output = append(output, page.DistributionConfigurationSummaryList...) } - d.SetId(meta.(*conns.AWSClient).Region) - d.Set(names.AttrARNs, arns) - d.Set(names.AttrNames, nms) - - return diags + return output, nil } diff --git a/internal/service/imagebuilder/errors.go b/internal/service/imagebuilder/errors.go new file mode 100644 index 00000000000..bed62e4db99 --- /dev/null +++ b/internal/service/imagebuilder/errors.go @@ -0,0 +1,13 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package imagebuilder + +import ( + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" +) + +var ( + errCodeInvalidParameterValueException = (*awstypes.InvalidParameterValueException)(nil).ErrorCode() + errCodeResourceNotFoundException = (*awstypes.ResourceNotFoundException)(nil).ErrorCode() +) diff --git a/internal/service/imagebuilder/exports_tests.go b/internal/service/imagebuilder/exports_tests.go new file mode 100644 index 00000000000..bbe48598121 --- /dev/null +++ b/internal/service/imagebuilder/exports_tests.go @@ -0,0 +1,27 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package imagebuilder + +// Exports for use in tests only. +var ( + ResourceComponent = resourceComponent + ResourceContainerRecipe = resourceContainerRecipe + ResourceDistributionConfiguration = resourceDistributionConfiguration + ResourceImage = resourceImage + ResourceImagePipeline = resourceImagePipeline + ResourceImageRecipe = resourceImageRecipe + ResourceInfrastructureConfiguration = resourceInfrastructureConfiguration + ResourceLifecyclePolicy = newLifecyclePolicyResource + ResourceWorkflow = resourceWorkflow + + FindComponentByARN = findComponentByARN + FindContainerRecipeByARN = findContainerRecipeByARN + FindDistributionConfigurationByARN = findDistributionConfigurationByARN + FindImageByARN = findImageByARN + FindImagePipelineByARN = findImagePipelineByARN + FindImageRecipeByARN = findImageRecipeByARN + FindInfrastructureConfigurationByARN = findInfrastructureConfigurationByARN + FindLifecyclePolicyByARN = findLifecyclePolicyByARN + FindWorkflowByARN = findWorkflowByARN +) diff --git a/internal/service/imagebuilder/flex.go b/internal/service/imagebuilder/flex.go index 572f2c7bf26..9ce953b1909 100644 --- a/internal/service/imagebuilder/flex.go +++ b/internal/service/imagebuilder/flex.go @@ -4,13 +4,13 @@ package imagebuilder import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/names" ) -func flattenWorkflowParameter(apiObject *imagebuilder.WorkflowParameter) map[string]interface{} { +func flattenWorkflowParameter(apiObject *awstypes.WorkflowParameter) map[string]interface{} { if apiObject == nil { return nil } @@ -18,19 +18,19 @@ func flattenWorkflowParameter(apiObject *imagebuilder.WorkflowParameter) map[str tfMap := map[string]interface{}{} if v := apiObject.Name; v != nil { - tfMap[names.AttrName] = aws.StringValue(v) + tfMap[names.AttrName] = aws.ToString(v) } - if v := apiObject.Value; v != nil { + if v := apiObject.Value; len(v) > 0 { // ImageBuilder API quirk // Even though Value is a slice, only one element is accepted. - tfMap[names.AttrValue] = aws.StringValueSlice(v)[0] + tfMap[names.AttrValue] = v[0] } return tfMap } -func flattenWorkflowParameters(apiObjects []*imagebuilder.WorkflowParameter) []interface{} { +func flattenWorkflowParameters(apiObjects []awstypes.WorkflowParameter) []interface{} { if len(apiObjects) == 0 { return nil } @@ -38,29 +38,23 @@ func flattenWorkflowParameters(apiObjects []*imagebuilder.WorkflowParameter) []i var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - - tfList = append(tfList, flattenWorkflowParameter(apiObject)) + tfList = append(tfList, flattenWorkflowParameter(&apiObject)) } return tfList } -func flattenWorkflowConfiguration(apiObject *imagebuilder.WorkflowConfiguration) map[string]interface{} { +func flattenWorkflowConfiguration(apiObject *awstypes.WorkflowConfiguration) map[string]interface{} { if apiObject == nil { return nil } - tfMap := map[string]interface{}{} - - if v := apiObject.OnFailure; v != nil { - tfMap["on_failure"] = aws.String(*v) + tfMap := map[string]interface{}{ + "on_failure": apiObject.OnFailure, } if v := apiObject.ParallelGroup; v != nil { - tfMap["parallel_group"] = aws.String(*v) + tfMap["parallel_group"] = aws.ToString(v) } if v := apiObject.Parameters; v != nil { @@ -68,13 +62,13 @@ func flattenWorkflowConfiguration(apiObject *imagebuilder.WorkflowConfiguration) } if v := apiObject.WorkflowArn; v != nil { - tfMap["workflow_arn"] = aws.StringValue(v) + tfMap["workflow_arn"] = aws.ToString(v) } return tfMap } -func flattenWorkflowConfigurations(apiObjects []*imagebuilder.WorkflowConfiguration) []interface{} { +func flattenWorkflowConfigurations(apiObjects []awstypes.WorkflowConfiguration) []interface{} { if len(apiObjects) == 0 { return nil } @@ -86,18 +80,18 @@ func flattenWorkflowConfigurations(apiObjects []*imagebuilder.WorkflowConfigurat continue } - tfList = append(tfList, flattenWorkflowConfiguration(apiObject)) + tfList = append(tfList, flattenWorkflowConfiguration(&apiObject)) } return tfList } -func expandWorkflowParameter(tfMap map[string]interface{}) *imagebuilder.WorkflowParameter { +func expandWorkflowParameter(tfMap map[string]interface{}) *awstypes.WorkflowParameter { if tfMap == nil { return nil } - apiObject := &imagebuilder.WorkflowParameter{} + apiObject := &awstypes.WorkflowParameter{} if v, ok := tfMap[names.AttrName].(string); ok && v != "" { apiObject.Name = aws.String(v) @@ -106,22 +100,21 @@ func expandWorkflowParameter(tfMap map[string]interface{}) *imagebuilder.Workflo if v, ok := tfMap[names.AttrValue].(string); ok && v != "" { // ImageBuilder API quirk // Even though Value is a slice, only one element is accepted. - apiObject.Value = aws.StringSlice([]string{v}) + apiObject.Value = []string{v} } return apiObject } -func expandWorkflowParameters(tfList []interface{}) []*imagebuilder.WorkflowParameter { +func expandWorkflowParameters(tfList []interface{}) []awstypes.WorkflowParameter { if len(tfList) == 0 { return nil } - var apiObjects []*imagebuilder.WorkflowParameter + var apiObjects []awstypes.WorkflowParameter for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) - if !ok { continue } @@ -132,21 +125,21 @@ func expandWorkflowParameters(tfList []interface{}) []*imagebuilder.WorkflowPara continue } - apiObjects = append(apiObjects, apiObject) + apiObjects = append(apiObjects, *apiObject) } return apiObjects } -func expandWorkflowConfiguration(tfMap map[string]interface{}) *imagebuilder.WorkflowConfiguration { +func expandWorkflowConfiguration(tfMap map[string]interface{}) *awstypes.WorkflowConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.WorkflowConfiguration{} + apiObject := &awstypes.WorkflowConfiguration{} if v, ok := tfMap["on_failure"].(string); ok && v != "" { - apiObject.OnFailure = aws.String(v) + apiObject.OnFailure = awstypes.OnWorkflowFailure(v) } if v, ok := tfMap["parallel_group"].(string); ok && v != "" { @@ -164,16 +157,15 @@ func expandWorkflowConfiguration(tfMap map[string]interface{}) *imagebuilder.Wor return apiObject } -func expandWorkflowConfigurations(tfList []interface{}) []*imagebuilder.WorkflowConfiguration { +func expandWorkflowConfigurations(tfList []interface{}) []awstypes.WorkflowConfiguration { if len(tfList) == 0 { return nil } - var apiObjects []*imagebuilder.WorkflowConfiguration + var apiObjects []awstypes.WorkflowConfiguration for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) - if !ok { continue } @@ -184,7 +176,7 @@ func expandWorkflowConfigurations(tfList []interface{}) []*imagebuilder.Workflow continue } - apiObjects = append(apiObjects, apiObject) + apiObjects = append(apiObjects, *apiObject) } return apiObjects diff --git a/internal/service/imagebuilder/generate.go b/internal/service/imagebuilder/generate.go index 61b3bd30050..27461940de7 100644 --- a/internal/service/imagebuilder/generate.go +++ b/internal/service/imagebuilder/generate.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//go:generate go run ../../generate/tags/main.go -ListTags -ServiceTagsMap -UpdateTags -AWSSDKVersion=1 +//go:generate go run ../../generate/tags/main.go -ListTags -KVTValues -ServiceTagsMap -UpdateTags -SkipTypesImp //go:generate go run ../../generate/servicepackage/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. diff --git a/internal/service/imagebuilder/id.go b/internal/service/imagebuilder/id.go deleted file mode 100644 index 7941a7dd559..00000000000 --- a/internal/service/imagebuilder/id.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package imagebuilder - -const ( - // Missing from upstream aws-sdk-go. - // /~https://github.com/aws/aws-sdk-go/issues/3751. - EBSVolumeTypeGP3 = "gp3" -) diff --git a/internal/service/imagebuilder/image.go b/internal/service/imagebuilder/image.go index 4a8391d4c40..dc6f261eb85 100644 --- a/internal/service/imagebuilder/image.go +++ b/internal/service/imagebuilder/image.go @@ -5,27 +5,32 @@ package imagebuilder import ( "context" + "errors" "log" "time" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_imagebuilder_image", name="Image") // @Tags(identifierAttribute="id") -func ResourceImage() *schema.Resource { +func resourceImage() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceImageCreate, ReadWithoutTimeout: resourceImageRead, @@ -226,10 +231,10 @@ func ResourceImage() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "on_failure": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(imagebuilder.OnWorkflowFailure_Values(), false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.OnWorkflowFailure](), }, "parallel_group": { Type: schema.TypeString, @@ -274,7 +279,7 @@ func ResourceImage() *schema.Resource { func resourceImageCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.CreateImageInput{ ClientToken: aws.String(id.UniqueId()), @@ -314,20 +319,16 @@ func resourceImageCreate(ctx context.Context, d *schema.ResourceData, meta inter input.Workflows = expandWorkflowConfigurations(v.(*schema.Set).List()) } - output, err := conn.CreateImageWithContext(ctx, input) + output, err := conn.CreateImage(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Image Builder Image: %s", err) } - if output == nil { - return sdkdiag.AppendErrorf(diags, "creating Image Builder Image: empty response") - } - - d.SetId(aws.StringValue(output.ImageBuildVersionArn)) + d.SetId(aws.ToString(output.ImageBuildVersionArn)) if _, err := waitImageStatusAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for Image Builder Image (%s) to become available: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "waiting for Image Builder Image (%s) create: %s", d.Id(), err) } return append(diags, resourceImageRead(ctx, d, meta)...) @@ -335,30 +336,20 @@ func resourceImageCreate(ctx context.Context, d *schema.ResourceData, meta inter func resourceImageRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetImageInput{ - ImageBuildVersionArn: aws.String(d.Id()), - } + image, err := findImageByARN(ctx, conn, d.Id()) - output, err := conn.GetImageWithContext(ctx, input) - - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Image Builder Image (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image (%s): %s", d.Id(), err) - } - - if output == nil || output.Image == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image (%s): empty response", d.Id()) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Image (%s): %s", d.Id(), err) } - image := output.Image - d.Set(names.AttrARN, image.Arn) if image.ContainerRecipe != nil { d.Set("container_recipe_arn", image.ContainerRecipe.Arn) @@ -373,12 +364,16 @@ func resourceImageRead(ctx context.Context, d *schema.ResourceData, meta interfa d.Set("image_recipe_arn", image.ImageRecipe.Arn) } if image.ImageScanningConfiguration != nil { - d.Set("image_scanning_configuration", []interface{}{flattenImageScanningConfiguration(image.ImageScanningConfiguration)}) + if err := d.Set("image_scanning_configuration", []interface{}{flattenImageScanningConfiguration(image.ImageScanningConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting image_scanning_configuration: %s", err) + } } else { d.Set("image_scanning_configuration", nil) } if image.ImageTestsConfiguration != nil { - d.Set("image_tests_configuration", []interface{}{flattenImageTestsConfiguration(image.ImageTestsConfiguration)}) + if err := d.Set("image_tests_configuration", []interface{}{flattenImageTestsConfiguration(image.ImageTestsConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting image_tests_configuration: %s", err) + } } else { d.Set("image_tests_configuration", nil) } @@ -388,14 +383,18 @@ func resourceImageRead(ctx context.Context, d *schema.ResourceData, meta interfa d.Set(names.AttrName, image.Name) d.Set("os_version", image.OsVersion) if image.OutputResources != nil { - d.Set("output_resources", []interface{}{flattenOutputResources(image.OutputResources)}) + if err := d.Set("output_resources", []interface{}{flattenOutputResources(image.OutputResources)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting output_resources: %s", err) + } } else { d.Set("output_resources", nil) } d.Set("platform", image.Platform) d.Set(names.AttrVersion, image.Version) if image.Workflows != nil { - d.Set("workflow", flattenWorkflowConfigurations(image.Workflows)) + if err := d.Set("workflow", flattenWorkflowConfigurations(image.Workflows)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting workflow: %s", err) + } } else { d.Set("workflow", nil) } @@ -415,15 +414,14 @@ func resourceImageUpdate(ctx context.Context, d *schema.ResourceData, meta inter func resourceImageDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.DeleteImageInput{ + log.Printf("[DEBUG] Deleting Image Builder Image: %s", d.Id()) + _, err := conn.DeleteImage(ctx, &imagebuilder.DeleteImageInput{ ImageBuildVersionArn: aws.String(d.Id()), - } + }) - _, err := conn.DeleteImageWithContext(ctx, input) - - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { return diags } @@ -434,7 +432,74 @@ func resourceImageDelete(ctx context.Context, d *schema.ResourceData, meta inter return diags } -func flattenOutputResources(apiObject *imagebuilder.OutputResources) map[string]interface{} { +func findImageByARN(ctx context.Context, conn *imagebuilder.Client, arn string) (*awstypes.Image, error) { + input := &imagebuilder.GetImageInput{ + ImageBuildVersionArn: aws.String(arn), + } + + output, err := conn.GetImage(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Image == nil || output.Image.State == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.Image, nil +} + +func statusImage(ctx context.Context, conn *imagebuilder.Client, arn string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findImageByARN(ctx, conn, arn) + + if tfresource.NotFound(err) { + return nil, string(awstypes.ImageStatusPending), nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.State.Status), nil + } +} + +func waitImageStatusAvailable(ctx context.Context, conn *imagebuilder.Client, arn string, timeout time.Duration) (*awstypes.Image, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice( + awstypes.ImageStatusBuilding, + awstypes.ImageStatusCreating, + awstypes.ImageStatusDistributing, + awstypes.ImageStatusIntegrating, + awstypes.ImageStatusPending, + awstypes.ImageStatusTesting, + ), + Target: enum.Slice(awstypes.ImageStatusAvailable), + Refresh: statusImage(ctx, conn, arn), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.Image); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.State.Reason))) + + return output, err + } + + return nil, err +} + +func flattenOutputResources(apiObject *awstypes.OutputResources) map[string]interface{} { if apiObject == nil { return nil } @@ -452,37 +517,33 @@ func flattenOutputResources(apiObject *imagebuilder.OutputResources) map[string] return tfMap } -func flattenAMI(apiObject *imagebuilder.Ami) map[string]interface{} { - if apiObject == nil { - return nil - } - +func flattenAMI(apiObject awstypes.Ami) map[string]interface{} { tfMap := map[string]interface{}{} if v := apiObject.AccountId; v != nil { - tfMap[names.AttrAccountID] = aws.StringValue(v) + tfMap[names.AttrAccountID] = aws.ToString(v) } if v := apiObject.Description; v != nil { - tfMap[names.AttrDescription] = aws.StringValue(v) + tfMap[names.AttrDescription] = aws.ToString(v) } if v := apiObject.Image; v != nil { - tfMap["image"] = aws.StringValue(v) + tfMap["image"] = aws.ToString(v) } if v := apiObject.Name; v != nil { - tfMap[names.AttrName] = aws.StringValue(v) + tfMap[names.AttrName] = aws.ToString(v) } if v := apiObject.Region; v != nil { - tfMap[names.AttrRegion] = aws.StringValue(v) + tfMap[names.AttrRegion] = aws.ToString(v) } return tfMap } -func flattenAMIs(apiObjects []*imagebuilder.Ami) []interface{} { +func flattenAMIs(apiObjects []awstypes.Ami) []interface{} { if len(apiObjects) == 0 { return nil } @@ -490,35 +551,27 @@ func flattenAMIs(apiObjects []*imagebuilder.Ami) []interface{} { var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - tfList = append(tfList, flattenAMI(apiObject)) } return tfList } -func flattenContainer(apiObject *imagebuilder.Container) map[string]interface{} { - if apiObject == nil { - return nil - } - +func flattenContainer(apiObject awstypes.Container) map[string]interface{} { tfMap := map[string]interface{}{} if v := apiObject.ImageUris; v != nil { - tfMap["image_uris"] = aws.StringValueSlice(v) + tfMap["image_uris"] = aws.StringSlice(v) } if v := apiObject.Region; v != nil { - tfMap[names.AttrRegion] = aws.StringValue(v) + tfMap[names.AttrRegion] = aws.ToString(v) } return tfMap } -func flattenContainers(apiObjects []*imagebuilder.Container) []interface{} { +func flattenContainers(apiObjects []awstypes.Container) []interface{} { if len(apiObjects) == 0 { return nil } @@ -526,10 +579,6 @@ func flattenContainers(apiObjects []*imagebuilder.Container) []interface{} { var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - tfList = append(tfList, flattenContainer(apiObject)) } diff --git a/internal/service/imagebuilder/image_data_source.go b/internal/service/imagebuilder/image_data_source.go index 6a588a6f77f..7e7eb2db642 100644 --- a/internal/service/imagebuilder/image_data_source.go +++ b/internal/service/imagebuilder/image_data_source.go @@ -6,8 +6,7 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -17,8 +16,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_image") -func DataSourceImage() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_image", name="Image") +// @Tags +func dataSourceImage() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceImageRead, @@ -179,80 +179,64 @@ func DataSourceImage() *schema.Resource { func dataSourceImageRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetImageInput{} - - if v, ok := d.GetOk(names.AttrARN); ok { - input.ImageBuildVersionArn = aws.String(v.(string)) - } - - output, err := conn.GetImageWithContext(ctx, input) + arn := d.Get(names.AttrARN).(string) + image, err := findImageByARN(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image: %s", err) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Image (%s): %s", arn, err) } - if output == nil || output.Image == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image: empty response") - } - - image := output.Image - - d.SetId(aws.StringValue(image.Arn)) - + d.SetId(aws.ToString(image.Arn)) // To prevent Terraform errors, only reset arn if not configured. // The configured ARN may contain x.x.x wildcards while the API returns // the full build version #.#.#/# suffix. if _, ok := d.GetOk(names.AttrARN); !ok { d.Set(names.AttrARN, image.Arn) } - d.Set("build_version_arn", image.Arn) - d.Set("date_created", image.DateCreated) - if image.ContainerRecipe != nil { d.Set("container_recipe_arn", image.ContainerRecipe.Arn) } - + d.Set("date_created", image.DateCreated) if image.DistributionConfiguration != nil { d.Set("distribution_configuration_arn", image.DistributionConfiguration.Arn) } - d.Set("enhanced_image_metadata_enabled", image.EnhancedImageMetadataEnabled) - if image.ImageRecipe != nil { d.Set("image_recipe_arn", image.ImageRecipe.Arn) } - if image.ImageScanningConfiguration != nil { - d.Set("image_scanning_configuration", []interface{}{flattenImageScanningConfiguration(image.ImageScanningConfiguration)}) + if err := d.Set("image_scanning_configuration", []interface{}{flattenImageScanningConfiguration(image.ImageScanningConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting image_scanning_configuration: %s", err) + } } else { d.Set("image_scanning_configuration", nil) } - if image.ImageTestsConfiguration != nil { - d.Set("image_tests_configuration", []interface{}{flattenImageTestsConfiguration(image.ImageTestsConfiguration)}) + if err := d.Set("image_tests_configuration", []interface{}{flattenImageTestsConfiguration(image.ImageTestsConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting image_tests_configuration: %s", err) + } } else { d.Set("image_tests_configuration", nil) } - if image.InfrastructureConfiguration != nil { d.Set("infrastructure_configuration_arn", image.InfrastructureConfiguration.Arn) } - d.Set(names.AttrName, image.Name) d.Set("platform", image.Platform) d.Set("os_version", image.OsVersion) - if image.OutputResources != nil { - d.Set("output_resources", []interface{}{flattenOutputResources(image.OutputResources)}) + if err := d.Set("output_resources", []interface{}{flattenOutputResources(image.OutputResources)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting output_resources: %s", err) + } } else { d.Set("output_resources", nil) } - - d.Set(names.AttrTags, KeyValueTags(ctx, image.Tags).IgnoreAWS().IgnoreConfig(meta.(*conns.AWSClient).IgnoreTagsConfig).Map()) d.Set(names.AttrVersion, image.Version) + setTagsOut(ctx, image.Tags) + return diags } diff --git a/internal/service/imagebuilder/image_data_source_test.go b/internal/service/imagebuilder/image_data_source_test.go index 2191ccfe9b8..6bd5236f945 100644 --- a/internal/service/imagebuilder/image_data_source_test.go +++ b/internal/service/imagebuilder/image_data_source_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" 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" @@ -40,7 +40,7 @@ func TestAccImageBuilderImageDataSource_ARN_aws(t *testing.T) { // nosemgrep:ci. resource.TestCheckResourceAttr(dataSourceName, names.AttrName, "Amazon Linux 2 x86"), resource.TestCheckResourceAttr(dataSourceName, "os_version", "Amazon Linux 2"), resource.TestCheckResourceAttr(dataSourceName, "output_resources.#", acctest.Ct1), - resource.TestCheckResourceAttr(dataSourceName, "platform", imagebuilder.PlatformLinux), + resource.TestCheckResourceAttr(dataSourceName, "platform", string(awstypes.PlatformLinux)), resource.TestCheckResourceAttr(dataSourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestMatchResourceAttr(dataSourceName, names.AttrVersion, regexache.MustCompile(`\d+\.\d+\.\d+/\d+`)), ), @@ -129,7 +129,7 @@ data "aws_imagebuilder_image" "test" { func testAccImageDataSourceConfig_arnSelf(rName string) string { return fmt.Sprintf(` data "aws_imagebuilder_component" "update-linux" { - arn = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:component/update-linux/1.0.0" + arn = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:component/update-linux/1.0.2" } data "aws_region" "current" {} @@ -330,7 +330,7 @@ resource "aws_ecr_repository" "test" { } data "aws_imagebuilder_component" "update-linux" { - arn = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:component/update-linux/1.0.0" + arn = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:component/update-linux/1.0.2" } resource "aws_imagebuilder_container_recipe" "test" { diff --git a/internal/service/imagebuilder/image_pipeline.go b/internal/service/imagebuilder/image_pipeline.go index 174bca68b69..167634f2cc0 100644 --- a/internal/service/imagebuilder/image_pipeline.go +++ b/internal/service/imagebuilder/image_pipeline.go @@ -8,29 +8,34 @@ import ( "log" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_imagebuilder_image_pipeline", name="Image Pipeline") // @Tags(identifierAttribute="id") -func ResourceImagePipeline() *schema.Resource { +func resourceImagePipeline() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceImagePipelineCreate, ReadWithoutTimeout: resourceImagePipelineRead, UpdateWithoutTimeout: resourceImagePipelineUpdate, DeleteWithoutTimeout: resourceImagePipelineDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -169,10 +174,10 @@ func ResourceImagePipeline() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "pipeline_execution_start_condition": { - Type: schema.TypeString, - Optional: true, - Default: imagebuilder.PipelineExecutionStartConditionExpressionMatchAndDependencyUpdatesAvailable, - ValidateFunc: validation.StringInSlice(imagebuilder.PipelineExecutionStartCondition_Values(), false), + Type: schema.TypeString, + Optional: true, + Default: string(awstypes.PipelineExecutionStartConditionExpressionMatchAndDependencyUpdatesAvailable), + ValidateDiagFunc: enum.Validate[awstypes.PipelineExecutionStartCondition](), }, names.AttrScheduleExpression: { Type: schema.TypeString, @@ -191,10 +196,10 @@ func ResourceImagePipeline() *schema.Resource { }, }, names.AttrStatus: { - Type: schema.TypeString, - Optional: true, - Default: imagebuilder.PipelineStatusEnabled, - ValidateFunc: validation.StringInSlice(imagebuilder.PipelineStatus_Values(), false), + Type: schema.TypeString, + Optional: true, + Default: string(awstypes.PipelineStatusEnabled), + ValidateDiagFunc: enum.Validate[awstypes.PipelineStatus](), }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), @@ -205,9 +210,9 @@ func ResourceImagePipeline() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "on_failure": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(imagebuilder.OnWorkflowFailure_Values(), false), + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[awstypes.OnWorkflowFailure](), }, "parallel_group": { Type: schema.TypeString, @@ -247,7 +252,7 @@ func ResourceImagePipeline() *schema.Resource { func resourceImagePipelineCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.CreateImagePipelineInput{ ClientToken: aws.String(id.UniqueId()), @@ -296,54 +301,40 @@ func resourceImagePipelineCreate(ctx context.Context, d *schema.ResourceData, me } if v, ok := d.GetOk(names.AttrStatus); ok { - input.Status = aws.String(v.(string)) + input.Status = awstypes.PipelineStatus(v.(string)) } if v, ok := d.GetOk("workflow"); ok && len(v.([]interface{})) > 0 { input.Workflows = expandWorkflowConfigurations(v.([]interface{})) } - output, err := conn.CreateImagePipelineWithContext(ctx, input) + output, err := conn.CreateImagePipeline(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Image Builder Image Pipeline: %s", err) } - if output == nil { - return sdkdiag.AppendErrorf(diags, "creating Image Builder Image Pipeline: empty response") - } - - d.SetId(aws.StringValue(output.ImagePipelineArn)) + d.SetId(aws.ToString(output.ImagePipelineArn)) return append(diags, resourceImagePipelineRead(ctx, d, meta)...) } func resourceImagePipelineRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetImagePipelineInput{ - ImagePipelineArn: aws.String(d.Id()), - } + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - output, err := conn.GetImagePipelineWithContext(ctx, input) + imagePipeline, err := findImagePipelineByARN(ctx, conn, d.Id()) - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Image Builder Image Pipeline (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image Pipeline (%s): %s", d.Id(), err) - } - - if output == nil || output.ImagePipeline == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image Pipeline (%s): empty response", d.Id()) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Pipeline (%s): %s", d.Id(), err) } - imagePipeline := output.ImagePipeline - d.Set(names.AttrARN, imagePipeline.Arn) d.Set("container_recipe_arn", imagePipeline.ContainerRecipeArn) d.Set("date_created", imagePipeline.DateCreated) @@ -356,12 +347,16 @@ func resourceImagePipelineRead(ctx context.Context, d *schema.ResourceData, meta d.Set("execution_role", imagePipeline.ExecutionRole) d.Set("image_recipe_arn", imagePipeline.ImageRecipeArn) if imagePipeline.ImageScanningConfiguration != nil { - d.Set("image_scanning_configuration", []interface{}{flattenImageScanningConfiguration(imagePipeline.ImageScanningConfiguration)}) + if err := d.Set("image_scanning_configuration", []interface{}{flattenImageScanningConfiguration(imagePipeline.ImageScanningConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting image scanning configuration: %s", err) + } } else { d.Set("image_scanning_configuration", nil) } if imagePipeline.ImageTestsConfiguration != nil { - d.Set("image_tests_configuration", []interface{}{flattenImageTestsConfiguration(imagePipeline.ImageTestsConfiguration)}) + if err := d.Set("image_tests_configuration", []interface{}{flattenImageTestsConfiguration(imagePipeline.ImageTestsConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting image tests configuration: %s", err) + } } else { d.Set("image_tests_configuration", nil) } @@ -369,12 +364,16 @@ func resourceImagePipelineRead(ctx context.Context, d *schema.ResourceData, meta d.Set(names.AttrName, imagePipeline.Name) d.Set("platform", imagePipeline.Platform) if imagePipeline.Schedule != nil { - d.Set(names.AttrSchedule, []interface{}{flattenSchedule(imagePipeline.Schedule)}) + if err := d.Set(names.AttrSchedule, []interface{}{flattenSchedule(imagePipeline.Schedule)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting schedule: %s", err) + } } else { d.Set(names.AttrSchedule, nil) } d.Set(names.AttrStatus, imagePipeline.Status) - d.Set("workflow", flattenWorkflowConfigurations(imagePipeline.Workflows)) + if err := d.Set("workflow", flattenWorkflowConfigurations(imagePipeline.Workflows)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting workflow: %s", err) + } setTagsOut(ctx, imagePipeline.Tags) @@ -383,7 +382,7 @@ func resourceImagePipelineRead(ctx context.Context, d *schema.ResourceData, meta func resourceImagePipelineUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) if d.HasChanges( names.AttrDescription, @@ -440,14 +439,14 @@ func resourceImagePipelineUpdate(ctx context.Context, d *schema.ResourceData, me } if v, ok := d.GetOk(names.AttrStatus); ok { - input.Status = aws.String(v.(string)) + input.Status = awstypes.PipelineStatus(v.(string)) } if v, ok := d.GetOk("workflow"); ok && len(v.([]interface{})) > 0 { input.Workflows = expandWorkflowConfigurations(v.([]interface{})) } - _, err := conn.UpdateImagePipelineWithContext(ctx, input) + _, err := conn.UpdateImagePipeline(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating Image Builder Image Pipeline (%s): %s", d.Id(), err) @@ -459,15 +458,14 @@ func resourceImagePipelineUpdate(ctx context.Context, d *schema.ResourceData, me func resourceImagePipelineDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.DeleteImagePipelineInput{ + log.Printf("[DEBUG] Deleting Image Builder Image Pipeline: %s", d.Id()) + _, err := conn.DeleteImagePipeline(ctx, &imagebuilder.DeleteImagePipelineInput{ ImagePipelineArn: aws.String(d.Id()), - } - - _, err := conn.DeleteImagePipelineWithContext(ctx, input) + }) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { return diags } @@ -478,12 +476,37 @@ func resourceImagePipelineDelete(ctx context.Context, d *schema.ResourceData, me return diags } -func expandImageScanningConfiguration(tfMap map[string]interface{}) *imagebuilder.ImageScanningConfiguration { +func findImagePipelineByARN(ctx context.Context, conn *imagebuilder.Client, arn string) (*awstypes.ImagePipeline, error) { + input := &imagebuilder.GetImagePipelineInput{ + ImagePipelineArn: aws.String(arn), + } + + output, err := conn.GetImagePipeline(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.ImagePipeline == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.ImagePipeline, nil +} + +func expandImageScanningConfiguration(tfMap map[string]interface{}) *awstypes.ImageScanningConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.ImageScanningConfiguration{} + apiObject := &awstypes.ImageScanningConfiguration{} if v, ok := tfMap["image_scanning_enabled"].(bool); ok { apiObject.ImageScanningEnabled = aws.Bool(v) @@ -496,15 +519,15 @@ func expandImageScanningConfiguration(tfMap map[string]interface{}) *imagebuilde return apiObject } -func expandECRConfiguration(tfMap map[string]interface{}) *imagebuilder.EcrConfiguration { +func expandECRConfiguration(tfMap map[string]interface{}) *awstypes.EcrConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.EcrConfiguration{} + apiObject := &awstypes.EcrConfiguration{} if v, ok := tfMap["container_tags"].(*schema.Set); ok { - apiObject.ContainerTags = flex.ExpandStringSet(v) + apiObject.ContainerTags = flex.ExpandStringValueSet(v) } if v, ok := tfMap[names.AttrRepositoryName].(string); ok { @@ -514,33 +537,33 @@ func expandECRConfiguration(tfMap map[string]interface{}) *imagebuilder.EcrConfi return apiObject } -func expandImageTestConfiguration(tfMap map[string]interface{}) *imagebuilder.ImageTestsConfiguration { +func expandImageTestConfiguration(tfMap map[string]interface{}) *awstypes.ImageTestsConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.ImageTestsConfiguration{} + apiObject := &awstypes.ImageTestsConfiguration{} if v, ok := tfMap["image_tests_enabled"].(bool); ok { apiObject.ImageTestsEnabled = aws.Bool(v) } if v, ok := tfMap["timeout_minutes"].(int); ok && v != 0 { - apiObject.TimeoutMinutes = aws.Int64(int64(v)) + apiObject.TimeoutMinutes = aws.Int32(int32(v)) } return apiObject } -func expandPipelineSchedule(tfMap map[string]interface{}) *imagebuilder.Schedule { +func expandPipelineSchedule(tfMap map[string]interface{}) *awstypes.Schedule { if tfMap == nil { return nil } - apiObject := &imagebuilder.Schedule{} + apiObject := &awstypes.Schedule{} if v, ok := tfMap["pipeline_execution_start_condition"].(string); ok && v != "" { - apiObject.PipelineExecutionStartCondition = aws.String(v) + apiObject.PipelineExecutionStartCondition = awstypes.PipelineExecutionStartCondition(v) } if v, ok := tfMap[names.AttrScheduleExpression].(string); ok && v != "" { @@ -554,7 +577,7 @@ func expandPipelineSchedule(tfMap map[string]interface{}) *imagebuilder.Schedule return apiObject } -func flattenImageScanningConfiguration(apiObject *imagebuilder.ImageScanningConfiguration) map[string]interface{} { +func flattenImageScanningConfiguration(apiObject *awstypes.ImageScanningConfiguration) map[string]interface{} { if apiObject == nil { return nil } @@ -562,7 +585,7 @@ func flattenImageScanningConfiguration(apiObject *imagebuilder.ImageScanningConf tfMap := map[string]interface{}{} if v := apiObject.ImageScanningEnabled; v != nil { - tfMap["image_scanning_enabled"] = aws.BoolValue(v) + tfMap["image_scanning_enabled"] = aws.ToBool(v) } if v := apiObject.EcrConfiguration; v != nil { @@ -572,7 +595,7 @@ func flattenImageScanningConfiguration(apiObject *imagebuilder.ImageScanningConf return tfMap } -func flattenECRConfiguration(apiObject *imagebuilder.EcrConfiguration) map[string]interface{} { +func flattenECRConfiguration(apiObject *awstypes.EcrConfiguration) map[string]interface{} { if apiObject == nil { return nil } @@ -580,17 +603,17 @@ func flattenECRConfiguration(apiObject *imagebuilder.EcrConfiguration) map[strin tfMap := map[string]interface{}{} if v := apiObject.RepositoryName; v != nil { - tfMap[names.AttrRepositoryName] = aws.StringValue(v) + tfMap[names.AttrRepositoryName] = aws.ToString(v) } if v := apiObject.ContainerTags; v != nil { - tfMap["container_tags"] = aws.StringValueSlice(v) + tfMap["container_tags"] = aws.StringSlice(v) } return tfMap } -func flattenImageTestsConfiguration(apiObject *imagebuilder.ImageTestsConfiguration) map[string]interface{} { +func flattenImageTestsConfiguration(apiObject *awstypes.ImageTestsConfiguration) map[string]interface{} { if apiObject == nil { return nil } @@ -598,33 +621,31 @@ func flattenImageTestsConfiguration(apiObject *imagebuilder.ImageTestsConfigurat tfMap := map[string]interface{}{} if v := apiObject.ImageTestsEnabled; v != nil { - tfMap["image_tests_enabled"] = aws.BoolValue(v) + tfMap["image_tests_enabled"] = aws.ToBool(v) } if v := apiObject.TimeoutMinutes; v != nil { - tfMap["timeout_minutes"] = aws.Int64Value(v) + tfMap["timeout_minutes"] = aws.ToInt32(v) } return tfMap } -func flattenSchedule(apiObject *imagebuilder.Schedule) map[string]interface{} { +func flattenSchedule(apiObject *awstypes.Schedule) map[string]interface{} { if apiObject == nil { return nil } tfMap := map[string]interface{}{} - if v := apiObject.PipelineExecutionStartCondition; v != nil { - tfMap["pipeline_execution_start_condition"] = aws.StringValue(v) - } + tfMap["pipeline_execution_start_condition"] = string(apiObject.PipelineExecutionStartCondition) if v := apiObject.ScheduleExpression; v != nil { - tfMap[names.AttrScheduleExpression] = aws.StringValue(v) + tfMap[names.AttrScheduleExpression] = aws.ToString(v) } if v := apiObject.Timezone; v != nil { - tfMap["timezone"] = aws.StringValue(v) + tfMap["timezone"] = aws.ToString(v) } return tfMap diff --git a/internal/service/imagebuilder/image_pipeline_data_source.go b/internal/service/imagebuilder/image_pipeline_data_source.go index fbba6496e9e..3829731a3e0 100644 --- a/internal/service/imagebuilder/image_pipeline_data_source.go +++ b/internal/service/imagebuilder/image_pipeline_data_source.go @@ -6,8 +6,7 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -17,8 +16,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_image_pipeline") -func DataSourceImagePipeline() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_image_pipeline", name="Image Pipeline") +// @Tags +func dataSourceImagePipeline() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceImagePipelineRead, @@ -150,27 +150,16 @@ func DataSourceImagePipeline() *schema.Resource { func dataSourceImagePipelineRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetImagePipelineInput{} - - if v, ok := d.GetOk(names.AttrARN); ok { - input.ImagePipelineArn = aws.String(v.(string)) - } - - output, err := conn.GetImagePipelineWithContext(ctx, input) + arn := d.Get(names.AttrARN).(string) + imagePipeline, err := findImagePipelineByARN(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image Pipeline: %s", err) - } - - if output == nil || output.ImagePipeline == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image Pipeline: empty response") + return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Pipeline (%s): %s", arn, err) } - imagePipeline := output.ImagePipeline - - d.SetId(aws.StringValue(imagePipeline.Arn)) + d.SetId(aws.ToString(imagePipeline.Arn)) d.Set(names.AttrARN, imagePipeline.Arn) d.Set("container_recipe_arn", imagePipeline.ContainerRecipeArn) d.Set("date_created", imagePipeline.DateCreated) @@ -182,12 +171,16 @@ func dataSourceImagePipelineRead(ctx context.Context, d *schema.ResourceData, me d.Set("enhanced_image_metadata_enabled", imagePipeline.EnhancedImageMetadataEnabled) d.Set("image_recipe_arn", imagePipeline.ImageRecipeArn) if imagePipeline.ImageScanningConfiguration != nil { - d.Set("image_scanning_configuration", []interface{}{flattenImageScanningConfiguration(imagePipeline.ImageScanningConfiguration)}) + if err := d.Set("image_scanning_configuration", []interface{}{flattenImageScanningConfiguration(imagePipeline.ImageScanningConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting image_scanning_configuration: %s", err) + } } else { d.Set("image_scanning_configuration", nil) } if imagePipeline.ImageTestsConfiguration != nil { - d.Set("image_tests_configuration", []interface{}{flattenImageTestsConfiguration(imagePipeline.ImageTestsConfiguration)}) + if err := d.Set("image_tests_configuration", []interface{}{flattenImageTestsConfiguration(imagePipeline.ImageTestsConfiguration)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting image_tests_configuration: %s", err) + } } else { d.Set("image_tests_configuration", nil) } @@ -195,13 +188,15 @@ func dataSourceImagePipelineRead(ctx context.Context, d *schema.ResourceData, me d.Set(names.AttrName, imagePipeline.Name) d.Set("platform", imagePipeline.Platform) if imagePipeline.Schedule != nil { - d.Set(names.AttrSchedule, []interface{}{flattenSchedule(imagePipeline.Schedule)}) + if err := d.Set(names.AttrSchedule, []interface{}{flattenSchedule(imagePipeline.Schedule)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting schedule: %s", err) + } } else { d.Set(names.AttrSchedule, nil) } - d.Set(names.AttrStatus, imagePipeline.Status) - d.Set(names.AttrTags, KeyValueTags(ctx, imagePipeline.Tags).IgnoreAWS().IgnoreConfig(meta.(*conns.AWSClient).IgnoreTagsConfig).Map()) + + setTagsOut(ctx, imagePipeline.Tags) return diags } diff --git a/internal/service/imagebuilder/image_pipeline_test.go b/internal/service/imagebuilder/image_pipeline_test.go index 61fd37dea1b..6b242880a10 100644 --- a/internal/service/imagebuilder/image_pipeline_test.go +++ b/internal/service/imagebuilder/image_pipeline_test.go @@ -9,15 +9,14 @@ import ( "strings" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfimagebuilder "github.com/hashicorp/terraform-provider-aws/internal/service/imagebuilder" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -53,9 +52,9 @@ func TestAccImageBuilderImagePipeline_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "image_tests_configuration.0.timeout_minutes", "720"), resource.TestCheckResourceAttrPair(resourceName, "infrastructure_configuration_arn", infrastructureConfigurationResourceName, names.AttrARN), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), - resource.TestCheckResourceAttr(resourceName, "platform", imagebuilder.PlatformLinux), + resource.TestCheckResourceAttr(resourceName, "platform", string(types.PlatformLinux)), resource.TestCheckResourceAttr(resourceName, "schedule.#", acctest.Ct0), - resource.TestCheckResourceAttr(resourceName, names.AttrStatus, imagebuilder.PipelineStatusEnabled), + resource.TestCheckResourceAttr(resourceName, names.AttrStatus, string(types.PipelineStatusEnabled)), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), ), }, @@ -464,11 +463,11 @@ func TestAccImageBuilderImagePipeline_Schedule_pipelineExecutionStartCondition(t CheckDestroy: testAccCheckImagePipelineDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccImagePipelineConfig_scheduleExecutionStartCondition(rName, imagebuilder.PipelineExecutionStartConditionExpressionMatchAndDependencyUpdatesAvailable), + Config: testAccImagePipelineConfig_scheduleExecutionStartCondition(rName, string(types.PipelineExecutionStartConditionExpressionMatchAndDependencyUpdatesAvailable)), Check: resource.ComposeTestCheckFunc( testAccCheckImagePipelineExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "schedule.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "schedule.0.pipeline_execution_start_condition", imagebuilder.PipelineExecutionStartConditionExpressionMatchAndDependencyUpdatesAvailable), + resource.TestCheckResourceAttr(resourceName, "schedule.0.pipeline_execution_start_condition", string(types.PipelineExecutionStartConditionExpressionMatchAndDependencyUpdatesAvailable)), ), }, { @@ -478,11 +477,11 @@ func TestAccImageBuilderImagePipeline_Schedule_pipelineExecutionStartCondition(t ImportStateVerifyIgnore: []string{"date_next_run"}, }, { - Config: testAccImagePipelineConfig_scheduleExecutionStartCondition(rName, imagebuilder.PipelineExecutionStartConditionExpressionMatchOnly), + Config: testAccImagePipelineConfig_scheduleExecutionStartCondition(rName, string(types.PipelineExecutionStartConditionExpressionMatchOnly)), Check: resource.ComposeTestCheckFunc( testAccCheckImagePipelineExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "schedule.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "schedule.0.pipeline_execution_start_condition", imagebuilder.PipelineExecutionStartConditionExpressionMatchOnly), + resource.TestCheckResourceAttr(resourceName, "schedule.0.pipeline_execution_start_condition", string(types.PipelineExecutionStartConditionExpressionMatchOnly)), ), }, }, @@ -577,10 +576,10 @@ func TestAccImageBuilderImagePipeline_status(t *testing.T) { CheckDestroy: testAccCheckImagePipelineDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccImagePipelineConfig_status(rName, imagebuilder.PipelineStatusDisabled), + Config: testAccImagePipelineConfig_status(rName, string(types.PipelineStatusDisabled)), Check: resource.ComposeTestCheckFunc( testAccCheckImagePipelineExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, names.AttrStatus, imagebuilder.PipelineStatusDisabled), + resource.TestCheckResourceAttr(resourceName, names.AttrStatus, string(types.PipelineStatusDisabled)), ), }, { @@ -589,10 +588,10 @@ func TestAccImageBuilderImagePipeline_status(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccImagePipelineConfig_status(rName, imagebuilder.PipelineStatusEnabled), + Config: testAccImagePipelineConfig_status(rName, string(types.PipelineStatusEnabled)), Check: resource.ComposeTestCheckFunc( testAccCheckImagePipelineExists(ctx, resourceName), - resource.TestCheckResourceAttr(resourceName, names.AttrStatus, imagebuilder.PipelineStatusEnabled), + resource.TestCheckResourceAttr(resourceName, names.AttrStatus, string(types.PipelineStatusEnabled)), ), }, }, @@ -656,11 +655,11 @@ func TestAccImageBuilderImagePipeline_workflow(t *testing.T) { CheckDestroy: testAccCheckImagePipelineDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccImagePipelineConfig_workflow(rName, imagebuilder.OnWorkflowFailureAbort, "test1"), + Config: testAccImagePipelineConfig_workflow(rName, string(types.OnWorkflowFailureAbort), "test1"), Check: resource.ComposeTestCheckFunc( testAccCheckImagePipelineExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "workflow.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "workflow.0.on_failure", imagebuilder.OnWorkflowFailureAbort), + resource.TestCheckResourceAttr(resourceName, "workflow.0.on_failure", string(types.OnWorkflowFailureAbort)), resource.TestCheckResourceAttr(resourceName, "workflow.0.parallel_group", "test1"), ), }, @@ -670,11 +669,11 @@ func TestAccImageBuilderImagePipeline_workflow(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccImagePipelineConfig_workflow(rName, imagebuilder.OnWorkflowFailureContinue, "test2"), + Config: testAccImagePipelineConfig_workflow(rName, string(types.OnWorkflowFailureContinue), "test2"), Check: resource.ComposeTestCheckFunc( testAccCheckImagePipelineExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "workflow.#", acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, "workflow.0.on_failure", imagebuilder.OnWorkflowFailureContinue), + resource.TestCheckResourceAttr(resourceName, "workflow.0.on_failure", string(types.OnWorkflowFailureContinue)), resource.TestCheckResourceAttr(resourceName, "workflow.0.parallel_group", "test2"), ), }, @@ -720,56 +719,42 @@ func TestAccImageBuilderImagePipeline_workflowParameter(t *testing.T) { func testAccCheckImagePipelineDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_imagebuilder_image_pipeline" { continue } - input := &imagebuilder.GetImagePipelineInput{ - ImagePipelineArn: aws.String(rs.Primary.ID), - } - - output, err := conn.GetImagePipelineWithContext(ctx, input) + _, err := tfimagebuilder.FindImagePipelineByARN(ctx, conn, rs.Primary.ID) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfresource.NotFound(err) { continue } if err != nil { - return fmt.Errorf("error getting Image Builder Image Pipeline (%s): %w", rs.Primary.ID, err) + return err } - if output != nil { - return fmt.Errorf("Image Builder Image Pipeline (%s) still exists", rs.Primary.ID) - } + return fmt.Errorf("Image Builder Image Pipeline %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckImagePipelineExists(ctx context.Context, resourceName string) resource.TestCheckFunc { +func testAccCheckImagePipelineExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("resource not found: %s", resourceName) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetImagePipelineInput{ - ImagePipelineArn: aws.String(rs.Primary.ID), + return fmt.Errorf("Not found: %s", n) } - _, err := conn.GetImagePipelineWithContext(ctx, input) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) - if err != nil { - return fmt.Errorf("error getting Image Builder Image Pipeline (%s): %w", rs.Primary.ID, err) - } + _, err := tfimagebuilder.FindImagePipelineByARN(ctx, conn, rs.Primary.ID) - return nil + return err } } diff --git a/internal/service/imagebuilder/image_pipelines_data_source.go b/internal/service/imagebuilder/image_pipelines_data_source.go index 0531e0f38ad..2df87c2c836 100644 --- a/internal/service/imagebuilder/image_pipelines_data_source.go +++ b/internal/service/imagebuilder/image_pipelines_data_source.go @@ -6,21 +6,24 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" - namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_image_pipelines") -func DataSourceImagePipelines() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_image_pipelines", name="Image Pipelines") +func dataSourceImagePipelines() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceImagePipelinesRead, + Schema: map[string]*schema.Schema{ names.AttrARNs: { Type: schema.TypeSet, @@ -39,46 +42,44 @@ func DataSourceImagePipelines() *schema.Resource { func dataSourceImagePipelinesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.ListImagePipelinesInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).ImageBuilderFilters() } - var results []*imagebuilder.ImagePipeline + imagePipelines, err := findImagePipelines(ctx, conn, input) - err := conn.ListImagePipelinesPagesWithContext(ctx, input, func(page *imagebuilder.ListImagePipelinesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Pipelines: %s", err) + } - for _, imagePipeline := range page.ImagePipelineList { - if imagePipeline == nil { - continue - } + d.SetId(meta.(*conns.AWSClient).Region) + d.Set(names.AttrARNs, tfslices.ApplyToAll(imagePipelines, func(v awstypes.ImagePipeline) string { + return aws.ToString(v.Arn) + })) + d.Set(names.AttrNames, tfslices.ApplyToAll(imagePipelines, func(v awstypes.ImagePipeline) string { + return aws.ToString(v.Name) + })) - results = append(results, imagePipeline) - } + return diags +} - return !lastPage - }) +func findImagePipelines(ctx context.Context, conn *imagebuilder.Client, input *imagebuilder.ListImagePipelinesInput) ([]awstypes.ImagePipeline, error) { + var output []awstypes.ImagePipeline - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Pipelines: %s", err) - } + pages := imagebuilder.NewListImagePipelinesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - var arns, nms []string + if err != nil { + return nil, err + } - for _, r := range results { - arns = append(arns, aws.StringValue(r.Arn)) - nms = append(nms, aws.StringValue(r.Name)) + output = append(output, page.ImagePipelineList...) } - d.SetId(meta.(*conns.AWSClient).Region) - d.Set(names.AttrARNs, arns) - d.Set(names.AttrNames, nms) - - return diags + return output, nil } diff --git a/internal/service/imagebuilder/image_recipe.go b/internal/service/imagebuilder/image_recipe.go index 1461a732cbe..3f61cb1c45f 100644 --- a/internal/service/imagebuilder/image_recipe.go +++ b/internal/service/imagebuilder/image_recipe.go @@ -8,29 +8,34 @@ import ( "log" "strconv" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/sdkv2/types/nullable" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_imagebuilder_image_recipe", name="Image Recipe") // @Tags(identifierAttribute="id") -func ResourceImageRecipe() *schema.Resource { +func resourceImageRecipe() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceImageRecipeCreate, ReadWithoutTimeout: resourceImageRecipeRead, UpdateWithoutTimeout: resourceImageRecipeUpdate, DeleteWithoutTimeout: resourceImageRecipeDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -104,10 +109,10 @@ func ResourceImageRecipe() *schema.Resource { ValidateFunc: validation.IntBetween(1, 16000), }, names.AttrVolumeType: { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(imagebuilder.EbsVolumeType_Values(), false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.EbsVolumeType](), }, }, }, @@ -118,6 +123,7 @@ func ResourceImageRecipe() *schema.Resource { // this is not compatible with TypeString's zero value. Type: schema.TypeBool, Optional: true, + Computed: true, ForceNew: true, }, names.AttrVirtualName: { @@ -242,7 +248,7 @@ func ResourceImageRecipe() *schema.Resource { func resourceImageRecipeCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.CreateImageRecipeInput{ ClientToken: aws.String(id.UniqueId()), @@ -270,14 +276,14 @@ func resourceImageRecipeCreate(ctx context.Context, d *schema.ResourceData, meta } if v, ok := d.GetOk("systems_manager_agent"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.AdditionalInstanceConfiguration = &imagebuilder.AdditionalInstanceConfiguration{ + input.AdditionalInstanceConfiguration = &awstypes.AdditionalInstanceConfiguration{ SystemsManagerAgent: expandSystemsManagerAgent(v.([]interface{})[0].(map[string]interface{})), } } if v, ok := d.GetOk("user_data_base64"); ok { if input.AdditionalInstanceConfiguration == nil { - input.AdditionalInstanceConfiguration = &imagebuilder.AdditionalInstanceConfiguration{} + input.AdditionalInstanceConfiguration = &awstypes.AdditionalInstanceConfiguration{} } input.AdditionalInstanceConfiguration.UserDataOverride = aws.String(v.(string)) } @@ -289,67 +295,57 @@ func resourceImageRecipeCreate(ctx context.Context, d *schema.ResourceData, meta input.WorkingDirectory = aws.String(v.(string)) } - output, err := conn.CreateImageRecipeWithContext(ctx, input) + output, err := conn.CreateImageRecipe(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Image Builder Image Recipe: %s", err) } - if output == nil { - return sdkdiag.AppendErrorf(diags, "creating Image Builder Image Recipe: empty response") - } - - d.SetId(aws.StringValue(output.ImageRecipeArn)) + d.SetId(aws.ToString(output.ImageRecipeArn)) return append(diags, resourceImageRecipeRead(ctx, d, meta)...) } func resourceImageRecipeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetImageRecipeInput{ - ImageRecipeArn: aws.String(d.Id()), - } - - output, err := conn.GetImageRecipeWithContext(ctx, input) + imageRecipe, err := findImageRecipeByARN(ctx, conn, d.Id()) - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Image Builder Image Recipe (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image Recipe (%s): %s", d.Id(), err) - } - - if output == nil || output.ImageRecipe == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Image Recipe (%s): empty response", d.Id()) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Recipe (%s): %s", d.Id(), err) } - imageRecipe := output.ImageRecipe - d.Set(names.AttrARN, imageRecipe.Arn) - d.Set("block_device_mapping", flattenInstanceBlockDeviceMappings(imageRecipe.BlockDeviceMappings)) - d.Set("component", flattenComponentConfigurations(imageRecipe.Components)) + if err := d.Set("block_device_mapping", flattenInstanceBlockDeviceMappings(imageRecipe.BlockDeviceMappings)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting block_device_mapping: %s", err) + } + if err := d.Set("component", flattenComponentConfigurations(imageRecipe.Components)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting component: %s", err) + } d.Set("date_created", imageRecipe.DateCreated) d.Set(names.AttrDescription, imageRecipe.Description) d.Set(names.AttrName, imageRecipe.Name) d.Set(names.AttrOwner, imageRecipe.Owner) d.Set("parent_image", imageRecipe.ParentImage) d.Set("platform", imageRecipe.Platform) - - setTagsOut(ctx, imageRecipe.Tags) - if imageRecipe.AdditionalInstanceConfiguration != nil { - d.Set("systems_manager_agent", []interface{}{flattenSystemsManagerAgent(imageRecipe.AdditionalInstanceConfiguration.SystemsManagerAgent)}) + if err := d.Set("systems_manager_agent", []interface{}{flattenSystemsManagerAgent(imageRecipe.AdditionalInstanceConfiguration.SystemsManagerAgent)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting systems_manager_agent: %s", err) + } d.Set("user_data_base64", imageRecipe.AdditionalInstanceConfiguration.UserDataOverride) } - d.Set(names.AttrVersion, imageRecipe.Version) d.Set("working_directory", imageRecipe.WorkingDirectory) + setTagsOut(ctx, imageRecipe.Tags) + return diags } @@ -363,15 +359,14 @@ func resourceImageRecipeUpdate(ctx context.Context, d *schema.ResourceData, meta func resourceImageRecipeDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.DeleteImageRecipeInput{ + log.Printf("[DEBUG] Deleting Image Builder Image Recipe: %s", d.Id()) + _, err := conn.DeleteImageRecipe(ctx, &imagebuilder.DeleteImageRecipeInput{ ImageRecipeArn: aws.String(d.Id()), - } - - _, err := conn.DeleteImageRecipeWithContext(ctx, input) + }) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { return diags } @@ -382,12 +377,37 @@ func resourceImageRecipeDelete(ctx context.Context, d *schema.ResourceData, meta return diags } -func expandComponentConfiguration(tfMap map[string]interface{}) *imagebuilder.ComponentConfiguration { +func findImageRecipeByARN(ctx context.Context, conn *imagebuilder.Client, arn string) (*awstypes.ImageRecipe, error) { + input := &imagebuilder.GetImageRecipeInput{ + ImageRecipeArn: aws.String(arn), + } + + output, err := conn.GetImageRecipe(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.ImageRecipe == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.ImageRecipe, nil +} + +func expandComponentConfiguration(tfMap map[string]interface{}) *awstypes.ComponentConfiguration { if tfMap == nil { return nil } - apiObject := &imagebuilder.ComponentConfiguration{} + apiObject := &awstypes.ComponentConfiguration{} if v, ok := tfMap["component_arn"].(string); ok && v != "" { apiObject.ComponentArn = aws.String(v) @@ -400,12 +420,12 @@ func expandComponentConfiguration(tfMap map[string]interface{}) *imagebuilder.Co return apiObject } -func expandComponentParameters(tfList []interface{}) []*imagebuilder.ComponentParameter { +func expandComponentParameters(tfList []interface{}) []awstypes.ComponentParameter { if len(tfList) == 0 { return nil } - var apiObjects []*imagebuilder.ComponentParameter + var apiObjects []awstypes.ComponentParameter for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) @@ -420,18 +440,18 @@ func expandComponentParameters(tfList []interface{}) []*imagebuilder.ComponentPa continue } - apiObjects = append(apiObjects, apiObject) + apiObjects = append(apiObjects, *apiObject) } return apiObjects } -func expandComponentParameter(tfMap map[string]interface{}) *imagebuilder.ComponentParameter { +func expandComponentParameter(tfMap map[string]interface{}) *awstypes.ComponentParameter { if tfMap == nil { return nil } - apiObject := &imagebuilder.ComponentParameter{} + apiObject := &awstypes.ComponentParameter{} if v, ok := tfMap[names.AttrName].(string); ok && v != "" { apiObject.Name = aws.String(v) @@ -440,18 +460,18 @@ func expandComponentParameter(tfMap map[string]interface{}) *imagebuilder.Compon if v, ok := tfMap[names.AttrValue].(string); ok && v != "" { // ImageBuilder API quirk // Even though Value is a slice, only one element is accepted. - apiObject.Value = aws.StringSlice([]string{v}) + apiObject.Value = []string{v} } return apiObject } -func expandComponentConfigurations(tfList []interface{}) []*imagebuilder.ComponentConfiguration { +func expandComponentConfigurations(tfList []interface{}) []awstypes.ComponentConfiguration { if len(tfList) == 0 { return nil } - var apiObjects []*imagebuilder.ComponentConfiguration + var apiObjects []awstypes.ComponentConfiguration for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) @@ -466,18 +486,18 @@ func expandComponentConfigurations(tfList []interface{}) []*imagebuilder.Compone continue } - apiObjects = append(apiObjects, apiObject) + apiObjects = append(apiObjects, *apiObject) } return apiObjects } -func expandEBSInstanceBlockDeviceSpecification(tfMap map[string]interface{}) *imagebuilder.EbsInstanceBlockDeviceSpecification { +func expandEBSInstanceBlockDeviceSpecification(tfMap map[string]interface{}) *awstypes.EbsInstanceBlockDeviceSpecification { if tfMap == nil { return nil } - apiObject := &imagebuilder.EbsInstanceBlockDeviceSpecification{} + apiObject := &awstypes.EbsInstanceBlockDeviceSpecification{} if v, null, _ := nullable.Bool(tfMap[names.AttrDeleteOnTermination].(string)).ValueBool(); !null { apiObject.DeleteOnTermination = aws.Bool(v) @@ -488,7 +508,7 @@ func expandEBSInstanceBlockDeviceSpecification(tfMap map[string]interface{}) *im } if v, ok := tfMap[names.AttrIOPS].(int); ok && v != 0 { - apiObject.Iops = aws.Int64(int64(v)) + apiObject.Iops = aws.Int32(int32(v)) } if v, ok := tfMap[names.AttrKMSKeyID].(string); ok && v != "" { @@ -500,26 +520,22 @@ func expandEBSInstanceBlockDeviceSpecification(tfMap map[string]interface{}) *im } if v, ok := tfMap[names.AttrThroughput].(int); ok && v != 0 { - apiObject.Throughput = aws.Int64(int64(v)) + apiObject.Throughput = aws.Int32(int32(v)) } if v, ok := tfMap[names.AttrVolumeSize].(int); ok && v != 0 { - apiObject.VolumeSize = aws.Int64(int64(v)) + apiObject.VolumeSize = aws.Int32(int32(v)) } if v, ok := tfMap[names.AttrVolumeType].(string); ok && v != "" { - apiObject.VolumeType = aws.String(v) + apiObject.VolumeType = awstypes.EbsVolumeType(v) } return apiObject } -func expandInstanceBlockDeviceMapping(tfMap map[string]interface{}) *imagebuilder.InstanceBlockDeviceMapping { - if tfMap == nil { - return nil - } - - apiObject := &imagebuilder.InstanceBlockDeviceMapping{} +func expandInstanceBlockDeviceMapping(tfMap map[string]interface{}) awstypes.InstanceBlockDeviceMapping { + apiObject := awstypes.InstanceBlockDeviceMapping{} if v, ok := tfMap[names.AttrDeviceName].(string); ok && v != "" { apiObject.DeviceName = aws.String(v) @@ -540,12 +556,12 @@ func expandInstanceBlockDeviceMapping(tfMap map[string]interface{}) *imagebuilde return apiObject } -func expandInstanceBlockDeviceMappings(tfList []interface{}) []*imagebuilder.InstanceBlockDeviceMapping { +func expandInstanceBlockDeviceMappings(tfList []interface{}) []awstypes.InstanceBlockDeviceMapping { if len(tfList) == 0 { return nil } - var apiObjects []*imagebuilder.InstanceBlockDeviceMapping + var apiObjects []awstypes.InstanceBlockDeviceMapping for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) @@ -554,24 +570,18 @@ func expandInstanceBlockDeviceMappings(tfList []interface{}) []*imagebuilder.Ins continue } - apiObject := expandInstanceBlockDeviceMapping(tfMap) - - if apiObject == nil { - continue - } - - apiObjects = append(apiObjects, apiObject) + apiObjects = append(apiObjects, expandInstanceBlockDeviceMapping(tfMap)) } return apiObjects } -func expandSystemsManagerAgent(tfMap map[string]interface{}) *imagebuilder.SystemsManagerAgent { +func expandSystemsManagerAgent(tfMap map[string]interface{}) *awstypes.SystemsManagerAgent { if tfMap == nil { return nil } - apiObject := &imagebuilder.SystemsManagerAgent{} + apiObject := &awstypes.SystemsManagerAgent{} if v, ok := tfMap["uninstall_after_build"].(bool); ok { apiObject.UninstallAfterBuild = aws.Bool(v) @@ -580,15 +590,11 @@ func expandSystemsManagerAgent(tfMap map[string]interface{}) *imagebuilder.Syste return apiObject } -func flattenComponentConfiguration(apiObject *imagebuilder.ComponentConfiguration) map[string]interface{} { - if apiObject == nil { - return nil - } - +func flattenComponentConfiguration(apiObject awstypes.ComponentConfiguration) map[string]interface{} { tfMap := map[string]interface{}{} if v := apiObject.ComponentArn; v != nil { - tfMap["component_arn"] = aws.StringValue(v) + tfMap["component_arn"] = aws.ToString(v) } if v := apiObject.Parameters; v != nil { @@ -598,7 +604,7 @@ func flattenComponentConfiguration(apiObject *imagebuilder.ComponentConfiguratio return tfMap } -func flattenComponentParameters(apiObjects []*imagebuilder.ComponentParameter) []interface{} { +func flattenComponentParameters(apiObjects []awstypes.ComponentParameter) []interface{} { if len(apiObjects) == 0 { return nil } @@ -606,37 +612,29 @@ func flattenComponentParameters(apiObjects []*imagebuilder.ComponentParameter) [ var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - tfList = append(tfList, flattenComponentParameter(apiObject)) } return tfList } -func flattenComponentParameter(apiObject *imagebuilder.ComponentParameter) map[string]interface{} { - if apiObject == nil { - return nil - } - +func flattenComponentParameter(apiObject awstypes.ComponentParameter) map[string]interface{} { tfMap := map[string]interface{}{} if v := apiObject.Name; v != nil { - tfMap[names.AttrName] = aws.StringValue(v) + tfMap[names.AttrName] = aws.ToString(v) } if v := apiObject.Value; v != nil { // ImageBuilder API quirk // Even though Value is a slice, only one element is accepted. - tfMap[names.AttrValue] = aws.StringValueSlice(v)[0] + tfMap[names.AttrValue] = aws.StringSlice(v)[0] } return tfMap } -func flattenComponentConfigurations(apiObjects []*imagebuilder.ComponentConfiguration) []interface{} { +func flattenComponentConfigurations(apiObjects []awstypes.ComponentConfiguration) []interface{} { if len(apiObjects) == 0 { return nil } @@ -644,17 +642,13 @@ func flattenComponentConfigurations(apiObjects []*imagebuilder.ComponentConfigur var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - tfList = append(tfList, flattenComponentConfiguration(apiObject)) } return tfList } -func flattenEBSInstanceBlockDeviceSpecification(apiObject *imagebuilder.EbsInstanceBlockDeviceSpecification) map[string]interface{} { +func flattenEBSInstanceBlockDeviceSpecification(apiObject *awstypes.EbsInstanceBlockDeviceSpecification) map[string]interface{} { if apiObject == nil { return nil } @@ -662,49 +656,43 @@ func flattenEBSInstanceBlockDeviceSpecification(apiObject *imagebuilder.EbsInsta tfMap := map[string]interface{}{} if v := apiObject.DeleteOnTermination; v != nil { - tfMap[names.AttrDeleteOnTermination] = strconv.FormatBool(aws.BoolValue(v)) + tfMap[names.AttrDeleteOnTermination] = strconv.FormatBool(aws.ToBool(v)) } if v := apiObject.Encrypted; v != nil { - tfMap[names.AttrEncrypted] = strconv.FormatBool(aws.BoolValue(v)) + tfMap[names.AttrEncrypted] = strconv.FormatBool(aws.ToBool(v)) } if v := apiObject.Iops; v != nil { - tfMap[names.AttrIOPS] = aws.Int64Value(v) + tfMap[names.AttrIOPS] = aws.ToInt32(v) } if v := apiObject.KmsKeyId; v != nil { - tfMap[names.AttrKMSKeyID] = aws.StringValue(v) + tfMap[names.AttrKMSKeyID] = aws.ToString(v) } if v := apiObject.SnapshotId; v != nil { - tfMap[names.AttrSnapshotID] = aws.StringValue(v) + tfMap[names.AttrSnapshotID] = aws.ToString(v) } if v := apiObject.Throughput; v != nil { - tfMap[names.AttrThroughput] = aws.Int64Value(v) + tfMap[names.AttrThroughput] = aws.ToInt32(v) } if v := apiObject.VolumeSize; v != nil { - tfMap[names.AttrVolumeSize] = aws.Int64Value(v) + tfMap[names.AttrVolumeSize] = aws.ToInt32(v) } - if v := apiObject.VolumeType; v != nil { - tfMap[names.AttrVolumeType] = aws.StringValue(v) - } + tfMap[names.AttrVolumeType] = apiObject.VolumeType return tfMap } -func flattenInstanceBlockDeviceMapping(apiObject *imagebuilder.InstanceBlockDeviceMapping) map[string]interface{} { - if apiObject == nil { - return nil - } - +func flattenInstanceBlockDeviceMapping(apiObject awstypes.InstanceBlockDeviceMapping) map[string]interface{} { tfMap := map[string]interface{}{} if v := apiObject.DeviceName; v != nil { - tfMap[names.AttrDeviceName] = aws.StringValue(v) + tfMap[names.AttrDeviceName] = aws.ToString(v) } if v := apiObject.Ebs; v != nil { @@ -716,13 +704,13 @@ func flattenInstanceBlockDeviceMapping(apiObject *imagebuilder.InstanceBlockDevi } if v := apiObject.VirtualName; v != nil { - tfMap[names.AttrVirtualName] = aws.StringValue(v) + tfMap[names.AttrVirtualName] = aws.ToString(v) } return tfMap } -func flattenInstanceBlockDeviceMappings(apiObjects []*imagebuilder.InstanceBlockDeviceMapping) []interface{} { +func flattenInstanceBlockDeviceMappings(apiObjects []awstypes.InstanceBlockDeviceMapping) []interface{} { if len(apiObjects) == 0 { return nil } @@ -730,17 +718,13 @@ func flattenInstanceBlockDeviceMappings(apiObjects []*imagebuilder.InstanceBlock var tfList []interface{} for _, apiObject := range apiObjects { - if apiObject == nil { - continue - } - tfList = append(tfList, flattenInstanceBlockDeviceMapping(apiObject)) } return tfList } -func flattenSystemsManagerAgent(apiObject *imagebuilder.SystemsManagerAgent) map[string]interface{} { +func flattenSystemsManagerAgent(apiObject *awstypes.SystemsManagerAgent) map[string]interface{} { if apiObject == nil { return nil } @@ -748,7 +732,7 @@ func flattenSystemsManagerAgent(apiObject *imagebuilder.SystemsManagerAgent) map tfMap := map[string]interface{}{} if v := apiObject.UninstallAfterBuild; v != nil { - tfMap["uninstall_after_build"] = aws.BoolValue(v) + tfMap["uninstall_after_build"] = aws.ToBool(v) } return tfMap diff --git a/internal/service/imagebuilder/image_recipe_data_source.go b/internal/service/imagebuilder/image_recipe_data_source.go index e46437854ca..9c2740e0074 100644 --- a/internal/service/imagebuilder/image_recipe_data_source.go +++ b/internal/service/imagebuilder/image_recipe_data_source.go @@ -6,8 +6,7 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -17,8 +16,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_image_recipe") -func DataSourceImageRecipe() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_image_recipe", name="Image Recipe") +// @Tags +func dataSourceImageRecipe() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceImageRecipeRead, @@ -140,7 +140,7 @@ func DataSourceImageRecipe() *schema.Resource { Type: schema.TypeString, Computed: true, }, - names.AttrTags: tftags.TagsSchema(), + names.AttrTags: tftags.TagsSchemaComputed(), "user_data_base64": { Type: schema.TypeString, Computed: true, @@ -159,45 +159,37 @@ func DataSourceImageRecipe() *schema.Resource { func dataSourceImageRecipeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetImageRecipeInput{} - - if v, ok := d.GetOk(names.AttrARN); ok { - input.ImageRecipeArn = aws.String(v.(string)) - } - - output, err := conn.GetImageRecipeWithContext(ctx, input) + arn := d.Get(names.AttrARN).(string) + imageRecipe, err := findImageRecipeByARN(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Recipe (%s): %s", aws.StringValue(input.ImageRecipeArn), err) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Recipe (%s): %s", arn, err) } - if output == nil || output.ImageRecipe == nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Recipe (%s): empty response", aws.StringValue(input.ImageRecipeArn)) + arn = aws.ToString(imageRecipe.Arn) + d.SetId(arn) + d.Set(names.AttrARN, arn) + if err := d.Set("block_device_mapping", flattenInstanceBlockDeviceMappings(imageRecipe.BlockDeviceMappings)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting block_device_mapping: %s", err) + } + if err := d.Set("component", flattenComponentConfigurations(imageRecipe.Components)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting component: %s", err) } - - imageRecipe := output.ImageRecipe - - d.SetId(aws.StringValue(imageRecipe.Arn)) - d.Set(names.AttrARN, imageRecipe.Arn) - d.Set("block_device_mapping", flattenInstanceBlockDeviceMappings(imageRecipe.BlockDeviceMappings)) - d.Set("component", flattenComponentConfigurations(imageRecipe.Components)) d.Set("date_created", imageRecipe.DateCreated) d.Set(names.AttrDescription, imageRecipe.Description) d.Set(names.AttrName, imageRecipe.Name) d.Set(names.AttrOwner, imageRecipe.Owner) d.Set("parent_image", imageRecipe.ParentImage) d.Set("platform", imageRecipe.Platform) - d.Set(names.AttrTags, KeyValueTags(ctx, imageRecipe.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()) - if imageRecipe.AdditionalInstanceConfiguration != nil { d.Set("user_data_base64", imageRecipe.AdditionalInstanceConfiguration.UserDataOverride) } - d.Set(names.AttrVersion, imageRecipe.Version) d.Set("working_directory", imageRecipe.WorkingDirectory) + setTagsOut(ctx, imageRecipe.Tags) + return diags } diff --git a/internal/service/imagebuilder/image_recipe_test.go b/internal/service/imagebuilder/image_recipe_test.go index 3033ea269aa..c5ae9497a75 100644 --- a/internal/service/imagebuilder/image_recipe_test.go +++ b/internal/service/imagebuilder/image_recipe_test.go @@ -9,15 +9,14 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfimagebuilder "github.com/hashicorp/terraform-provider-aws/internal/service/imagebuilder" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" itypes "github.com/hashicorp/terraform-provider-aws/internal/types" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -45,7 +44,7 @@ func TestAccImageBuilderImageRecipe_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), acctest.CheckResourceAttrAccountID(resourceName, names.AttrOwner), acctest.CheckResourceAttrRegionalARNAccountID(resourceName, "parent_image", "imagebuilder", "aws", "image/amazon-linux-2-x86/x.x.x"), - resource.TestCheckResourceAttr(resourceName, "platform", imagebuilder.PlatformLinux), + resource.TestCheckResourceAttr(resourceName, "platform", string(awstypes.PlatformLinux)), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), resource.TestCheckResourceAttr(resourceName, names.AttrVersion, "1.0.0"), ), @@ -332,12 +331,12 @@ func TestAccImageBuilderImageRecipe_BlockDeviceMappingEBS_volumeTypeGP2(t *testi CheckDestroy: testAccCheckImageRecipeDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccImageRecipeConfig_blockDeviceMappingEBSVolumeType(rName, imagebuilder.EbsVolumeTypeGp2), + Config: testAccImageRecipeConfig_blockDeviceMappingEBSVolumeType(rName, string(awstypes.EbsVolumeTypeGp2)), Check: resource.ComposeTestCheckFunc( testAccCheckImageRecipeExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "block_device_mapping.#", acctest.Ct1), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "block_device_mapping.*", map[string]string{ - "ebs.0.volume_type": imagebuilder.EbsVolumeTypeGp2, + "ebs.0.volume_type": string(awstypes.EbsVolumeTypeGp2), }), ), }, @@ -362,12 +361,12 @@ func TestAccImageBuilderImageRecipe_BlockDeviceMappingEBS_volumeTypeGP3(t *testi CheckDestroy: testAccCheckImageRecipeDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccImageRecipeConfig_blockDeviceMappingEBSVolumeType(rName, tfimagebuilder.EBSVolumeTypeGP3), + Config: testAccImageRecipeConfig_blockDeviceMappingEBSVolumeType(rName, string(awstypes.EbsVolumeTypeGp3)), Check: resource.ComposeTestCheckFunc( testAccCheckImageRecipeExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "block_device_mapping.#", acctest.Ct1), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "block_device_mapping.*", map[string]string{ - "ebs.0.volume_type": tfimagebuilder.EBSVolumeTypeGP3, + "ebs.0.volume_type": string(awstypes.EbsVolumeTypeGp3), }), ), }, @@ -742,56 +741,42 @@ func TestAccImageBuilderImageRecipe_windowsBaseImage(t *testing.T) { func testAccCheckImageRecipeDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_imagebuilder_image_recipe" { continue } - input := &imagebuilder.GetImageRecipeInput{ - ImageRecipeArn: aws.String(rs.Primary.ID), - } - - output, err := conn.GetImageRecipeWithContext(ctx, input) + _, err := tfimagebuilder.FindImageRecipeByARN(ctx, conn, rs.Primary.ID) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfresource.NotFound(err) { continue } if err != nil { - return fmt.Errorf("error getting Image Builder Image Recipe (%s): %w", rs.Primary.ID, err) + return err } - if output != nil { - return fmt.Errorf("Image Builder Image Recipe (%s) still exists", rs.Primary.ID) - } + return fmt.Errorf("Image Builder Image Recipe %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckImageRecipeExists(ctx context.Context, resourceName string) resource.TestCheckFunc { +func testAccCheckImageRecipeExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("resource not found: %s", resourceName) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetImageRecipeInput{ - ImageRecipeArn: aws.String(rs.Primary.ID), + return fmt.Errorf("Not found: %s", n) } - _, err := conn.GetImageRecipeWithContext(ctx, input) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) - if err != nil { - return fmt.Errorf("error getting Image Builder Image Recipe (%s): %w", rs.Primary.ID, err) - } + _, err := tfimagebuilder.FindImageRecipeByARN(ctx, conn, rs.Primary.ID) - return nil + return err } } diff --git a/internal/service/imagebuilder/image_recipes_data_source.go b/internal/service/imagebuilder/image_recipes_data_source.go index 9294eb90f40..a4342b16a8b 100644 --- a/internal/service/imagebuilder/image_recipes_data_source.go +++ b/internal/service/imagebuilder/image_recipes_data_source.go @@ -6,22 +6,25 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" - namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKDataSource("aws_imagebuilder_image_recipes", name="Image Recipes") -func DataSourceImageRecipes() *schema.Resource { +func dataSourceImageRecipes() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceImageRecipesRead, + Schema: map[string]*schema.Schema{ names.AttrARNs: { Type: schema.TypeSet, @@ -35,9 +38,9 @@ func DataSourceImageRecipes() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, names.AttrOwner: { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(imagebuilder.Ownership_Values(), false), + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[awstypes.Ownership](), }, }, } @@ -45,50 +48,48 @@ func DataSourceImageRecipes() *schema.Resource { func dataSourceImageRecipesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.ListImageRecipesInput{} - if v, ok := d.GetOk(names.AttrOwner); ok { - input.Owner = aws.String(v.(string)) + if v, ok := d.GetOk(names.AttrFilter); ok { + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).ImageBuilderFilters() } - if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() + if v, ok := d.GetOk(names.AttrOwner); ok { + input.Owner = awstypes.Ownership(v.(string)) } - var results []*imagebuilder.ImageRecipeSummary + imageRecipes, err := findImageRecipes(ctx, conn, input) - err := conn.ListImageRecipesPagesWithContext(ctx, input, func(page *imagebuilder.ListImageRecipesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Recipes: %s", err) + } - for _, imageRecipeSummary := range page.ImageRecipeSummaryList { - if imageRecipeSummary == nil { - continue - } + d.SetId(meta.(*conns.AWSClient).Region) + d.Set(names.AttrARNs, tfslices.ApplyToAll(imageRecipes, func(v awstypes.ImageRecipeSummary) string { + return aws.ToString(v.Arn) + })) + d.Set(names.AttrNames, tfslices.ApplyToAll(imageRecipes, func(v awstypes.ImageRecipeSummary) string { + return aws.ToString(v.Name) + })) - results = append(results, imageRecipeSummary) - } + return diags +} - return !lastPage - }) +func findImageRecipes(ctx context.Context, conn *imagebuilder.Client, input *imagebuilder.ListImageRecipesInput) ([]awstypes.ImageRecipeSummary, error) { + var output []awstypes.ImageRecipeSummary - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Image Recipes: %s", err) - } + pages := imagebuilder.NewListImageRecipesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - var arns, nms []string + if err != nil { + return nil, err + } - for _, r := range results { - arns = append(arns, aws.StringValue(r.Arn)) - nms = append(nms, aws.StringValue(r.Name)) + output = append(output, page.ImageRecipeSummaryList...) } - d.SetId(meta.(*conns.AWSClient).Region) - d.Set(names.AttrARNs, arns) - d.Set(names.AttrNames, nms) - - return diags + return output, nil } diff --git a/internal/service/imagebuilder/image_test.go b/internal/service/imagebuilder/image_test.go index 5440c92a0f6..1d0cee59615 100644 --- a/internal/service/imagebuilder/image_test.go +++ b/internal/service/imagebuilder/image_test.go @@ -9,15 +9,14 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfimagebuilder "github.com/hashicorp/terraform-provider-aws/internal/service/imagebuilder" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -49,7 +48,7 @@ func TestAccImageBuilderImage_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "image_tests_configuration.0.timeout_minutes", "720"), resource.TestCheckResourceAttrPair(resourceName, "infrastructure_configuration_arn", infrastructureConfigurationResourceName, names.AttrARN), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), - resource.TestCheckResourceAttr(resourceName, "platform", imagebuilder.PlatformLinux), + resource.TestCheckResourceAttr(resourceName, "platform", string(types.PlatformLinux)), resource.TestCheckResourceAttr(resourceName, "os_version", "Amazon Linux 2"), resource.TestCheckResourceAttr(resourceName, "output_resources.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), @@ -350,63 +349,49 @@ func TestAccImageBuilderImage_workflows(t *testing.T) { func testAccCheckImageDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_imagebuilder_image_pipeline" { continue } - input := &imagebuilder.GetImageInput{ - ImageBuildVersionArn: aws.String(rs.Primary.ID), - } - - output, err := conn.GetImageWithContext(ctx, input) + _, err := tfimagebuilder.FindImageByARN(ctx, conn, rs.Primary.ID) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfresource.NotFound(err) { continue } if err != nil { - return fmt.Errorf("error getting Image Builder Image (%s): %w", rs.Primary.ID, err) + return err } - if output != nil { - return fmt.Errorf("Image Builder Image (%s) still exists", rs.Primary.ID) - } + return fmt.Errorf("Image Builder Image %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckImageExists(ctx context.Context, resourceName string) resource.TestCheckFunc { +func testAccCheckImageExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("resource not found: %s", resourceName) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetImageInput{ - ImageBuildVersionArn: aws.String(rs.Primary.ID), + return fmt.Errorf("Not found: %s", n) } - _, err := conn.GetImageWithContext(ctx, input) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) - if err != nil { - return fmt.Errorf("error getting Image Builder Image (%s): %w", rs.Primary.ID, err) - } + _, err := tfimagebuilder.FindImageByARN(ctx, conn, rs.Primary.ID) - return nil + return err } } func testAccImageBaseConfig(rName string) string { return fmt.Sprintf(` data "aws_imagebuilder_component" "update-linux" { - arn = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:component/update-linux/1.0.0" + arn = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:component/update-linux/1.0.2" } data "aws_region" "current" {} @@ -938,7 +923,7 @@ resource "aws_ecr_repository" "test" { } data "aws_imagebuilder_component" "update-linux" { - arn = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:component/update-linux/1.0.0" + arn = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:component/update-linux/1.0.2" } resource "aws_imagebuilder_container_recipe" "test" { diff --git a/internal/service/imagebuilder/infrastructure_configuration.go b/internal/service/imagebuilder/infrastructure_configuration.go index ae93368dfbc..e623435d254 100644 --- a/internal/service/imagebuilder/infrastructure_configuration.go +++ b/internal/service/imagebuilder/infrastructure_configuration.go @@ -7,15 +7,17 @@ import ( "context" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -26,12 +28,13 @@ import ( // @SDKResource("aws_imagebuilder_infrastructure_configuration", name="Infrastructure Configuration") // @Tags(identifierAttribute="id") -func ResourceInfrastructureConfiguration() *schema.Resource { +func resourceInfrastructureConfiguration() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceInfrastructureConfigurationCreate, ReadWithoutTimeout: resourceInfrastructureConfigurationRead, UpdateWithoutTimeout: resourceInfrastructureConfigurationUpdate, DeleteWithoutTimeout: resourceInfrastructureConfigurationDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -155,7 +158,7 @@ func ResourceInfrastructureConfiguration() *schema.Resource { func resourceInfrastructureConfigurationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.CreateInfrastructureConfigurationInput{ ClientToken: aws.String(id.UniqueId()), @@ -176,7 +179,7 @@ func resourceInfrastructureConfigurationCreate(ctx context.Context, d *schema.Re } if v, ok := d.GetOk("instance_types"); ok && v.(*schema.Set).Len() > 0 { - input.InstanceTypes = flex.ExpandStringSet(v.(*schema.Set)) + input.InstanceTypes = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("key_pair"); ok { @@ -196,7 +199,7 @@ func resourceInfrastructureConfigurationCreate(ctx context.Context, d *schema.Re } if v, ok := d.GetOk(names.AttrSecurityGroupIDs); ok && v.(*schema.Set).Len() > 0 { - input.SecurityGroupIds = flex.ExpandStringSet(v.(*schema.Set)) + input.SecurityGroupIds = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk(names.AttrSNSTopicARN); ok { @@ -207,103 +210,80 @@ func resourceInfrastructureConfigurationCreate(ctx context.Context, d *schema.Re input.SubnetId = aws.String(v.(string)) } - var output *imagebuilder.CreateInfrastructureConfigurationOutput - err := retry.RetryContext(ctx, propagationTimeout, func() *retry.RetryError { - var err error - - output, err = conn.CreateInfrastructureConfigurationWithContext(ctx, input) - - if tfawserr.ErrMessageContains(err, imagebuilder.ErrCodeInvalidParameterValueException, "instance profile does not exist") { - return retry.RetryableError(err) - } - - if err != nil { - return retry.NonRetryableError(err) - } - - return nil - }) + outputRaw, err := tfresource.RetryWhen(ctx, propagationTimeout, + func() (interface{}, error) { + return conn.CreateInfrastructureConfiguration(ctx, input) + }, + func(err error) (bool, error) { + if errs.Contains(err, "instance profile does not exist") { + return true, err + } - if tfresource.TimedOut(err) { - output, err = conn.CreateInfrastructureConfigurationWithContext(ctx, input) - } + return false, err + }, + ) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Image Builder Infrastructure Configuration: %s", err) } - if output == nil { - return sdkdiag.AppendErrorf(diags, "creating Image Builder Infrastructure Configuration: empty response") - } - - d.SetId(aws.StringValue(output.InfrastructureConfigurationArn)) + d.SetId(aws.ToString(outputRaw.(*imagebuilder.CreateInfrastructureConfigurationOutput).InfrastructureConfigurationArn)) return append(diags, resourceInfrastructureConfigurationRead(ctx, d, meta)...) } func resourceInfrastructureConfigurationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetInfrastructureConfigurationInput{ - InfrastructureConfigurationArn: aws.String(d.Id()), - } + infrastructureConfiguration, err := findInfrastructureConfigurationByARN(ctx, conn, d.Id()) - output, err := conn.GetInfrastructureConfigurationWithContext(ctx, input) - - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Image Builder Infrastructure Configuration (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Infrastructure Configuration (%s): %s", d.Id(), err) - } - - if output == nil || output.InfrastructureConfiguration == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Infrastructure Configuration (%s): empty response", d.Id()) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Infrastructure Configuration (%s): %s", d.Id(), err) } - infrastructureConfiguration := output.InfrastructureConfiguration - d.Set(names.AttrARN, infrastructureConfiguration.Arn) d.Set("date_created", infrastructureConfiguration.DateCreated) d.Set("date_updated", infrastructureConfiguration.DateUpdated) d.Set(names.AttrDescription, infrastructureConfiguration.Description) - if infrastructureConfiguration.InstanceMetadataOptions != nil { - d.Set("instance_metadata_options", []interface{}{ - flattenInstanceMetadataOptions(infrastructureConfiguration.InstanceMetadataOptions), - }) + if err := d.Set("instance_metadata_options", []interface{}{flattenInstanceMetadataOptions(infrastructureConfiguration.InstanceMetadataOptions)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting instance_metadata_options: %s", err) + } } else { d.Set("instance_metadata_options", nil) } - d.Set("instance_profile_name", infrastructureConfiguration.InstanceProfileName) - d.Set("instance_types", aws.StringValueSlice(infrastructureConfiguration.InstanceTypes)) + d.Set("instance_types", infrastructureConfiguration.InstanceTypes) d.Set("key_pair", infrastructureConfiguration.KeyPair) if infrastructureConfiguration.Logging != nil { - d.Set("logging", []interface{}{flattenLogging(infrastructureConfiguration.Logging)}) + if err := d.Set("logging", []interface{}{flattenLogging(infrastructureConfiguration.Logging)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting logging: %s", err) + } } else { d.Set("logging", nil) } d.Set(names.AttrName, infrastructureConfiguration.Name) d.Set(names.AttrResourceTags, KeyValueTags(ctx, infrastructureConfiguration.ResourceTags).Map()) - d.Set(names.AttrSecurityGroupIDs, aws.StringValueSlice(infrastructureConfiguration.SecurityGroupIds)) + d.Set(names.AttrSecurityGroupIDs, infrastructureConfiguration.SecurityGroupIds) d.Set(names.AttrSNSTopicARN, infrastructureConfiguration.SnsTopicArn) d.Set(names.AttrSubnetID, infrastructureConfiguration.SubnetId) + d.Set("terminate_instance_on_failure", infrastructureConfiguration.TerminateInstanceOnFailure) setTagsOut(ctx, infrastructureConfiguration.Tags) - d.Set("terminate_instance_on_failure", infrastructureConfiguration.TerminateInstanceOnFailure) - return diags } func resourceInfrastructureConfigurationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) if d.HasChanges( names.AttrDescription, @@ -336,7 +316,7 @@ func resourceInfrastructureConfigurationUpdate(ctx context.Context, d *schema.Re } if v, ok := d.GetOk("instance_types"); ok && v.(*schema.Set).Len() > 0 { - input.InstanceTypes = flex.ExpandStringSet(v.(*schema.Set)) + input.InstanceTypes = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk("key_pair"); ok { @@ -352,7 +332,7 @@ func resourceInfrastructureConfigurationUpdate(ctx context.Context, d *schema.Re } if v, ok := d.GetOk(names.AttrSecurityGroupIDs); ok && v.(*schema.Set).Len() > 0 { - input.SecurityGroupIds = flex.ExpandStringSet(v.(*schema.Set)) + input.SecurityGroupIds = flex.ExpandStringValueSet(v.(*schema.Set)) } if v, ok := d.GetOk(names.AttrSNSTopicARN); ok { @@ -363,23 +343,18 @@ func resourceInfrastructureConfigurationUpdate(ctx context.Context, d *schema.Re input.SubnetId = aws.String(v.(string)) } - err := retry.RetryContext(ctx, propagationTimeout, func() *retry.RetryError { - _, err := conn.UpdateInfrastructureConfigurationWithContext(ctx, input) - - if tfawserr.ErrMessageContains(err, imagebuilder.ErrCodeInvalidParameterValueException, "instance profile does not exist") { - return retry.RetryableError(err) - } - - if err != nil { - return retry.NonRetryableError(err) - } - - return nil - }) + _, err := tfresource.RetryWhen(ctx, propagationTimeout, + func() (interface{}, error) { + return conn.UpdateInfrastructureConfiguration(ctx, input) + }, + func(err error) (bool, error) { + if errs.Contains(err, "instance profile does not exist") { + return true, err + } - if tfresource.TimedOut(err) { - _, err = conn.UpdateInfrastructureConfigurationWithContext(ctx, input) - } + return false, err + }, + ) if err != nil { return sdkdiag.AppendErrorf(diags, "updating Image Builder Infrastructure Configuration (%s): %s", d.Id(), err) @@ -391,15 +366,14 @@ func resourceInfrastructureConfigurationUpdate(ctx context.Context, d *schema.Re func resourceInfrastructureConfigurationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.DeleteInfrastructureConfigurationInput{ + log.Printf("[DEBUG] Deleting Image Infrastructure Configuration: %s", d.Id()) + _, err := conn.DeleteInfrastructureConfiguration(ctx, &imagebuilder.DeleteInfrastructureConfigurationInput{ InfrastructureConfigurationArn: aws.String(d.Id()), - } - - _, err := conn.DeleteInfrastructureConfigurationWithContext(ctx, input) + }) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { return diags } @@ -410,15 +384,40 @@ func resourceInfrastructureConfigurationDelete(ctx context.Context, d *schema.Re return diags } -func expandInstanceMetadataOptions(tfMap map[string]interface{}) *imagebuilder.InstanceMetadataOptions { +func findInfrastructureConfigurationByARN(ctx context.Context, conn *imagebuilder.Client, arn string) (*awstypes.InfrastructureConfiguration, error) { + input := &imagebuilder.GetInfrastructureConfigurationInput{ + InfrastructureConfigurationArn: aws.String(arn), + } + + output, err := conn.GetInfrastructureConfiguration(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.InfrastructureConfiguration == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.InfrastructureConfiguration, nil +} + +func expandInstanceMetadataOptions(tfMap map[string]interface{}) *awstypes.InstanceMetadataOptions { if tfMap == nil { return nil } - apiObject := &imagebuilder.InstanceMetadataOptions{} + apiObject := &awstypes.InstanceMetadataOptions{} if v, ok := tfMap["http_put_response_hop_limit"].(int); ok && v != 0 { - apiObject.HttpPutResponseHopLimit = aws.Int64(int64(v)) + apiObject.HttpPutResponseHopLimit = aws.Int32(int32(v)) } if v, ok := tfMap["http_tokens"].(string); ok && v != "" { @@ -428,12 +427,12 @@ func expandInstanceMetadataOptions(tfMap map[string]interface{}) *imagebuilder.I return apiObject } -func expandLogging(tfMap map[string]interface{}) *imagebuilder.Logging { +func expandLogging(tfMap map[string]interface{}) *awstypes.Logging { if tfMap == nil { return nil } - apiObject := &imagebuilder.Logging{} + apiObject := &awstypes.Logging{} if v, ok := tfMap["s3_logs"].([]interface{}); ok && len(v) > 0 && v[0] != nil { apiObject.S3Logs = expandS3Logs(v[0].(map[string]interface{})) @@ -442,12 +441,12 @@ func expandLogging(tfMap map[string]interface{}) *imagebuilder.Logging { return apiObject } -func expandS3Logs(tfMap map[string]interface{}) *imagebuilder.S3Logs { +func expandS3Logs(tfMap map[string]interface{}) *awstypes.S3Logs { if tfMap == nil { return nil } - apiObject := &imagebuilder.S3Logs{} + apiObject := &awstypes.S3Logs{} if v, ok := tfMap[names.AttrS3BucketName].(string); ok && v != "" { apiObject.S3BucketName = aws.String(v) @@ -460,7 +459,7 @@ func expandS3Logs(tfMap map[string]interface{}) *imagebuilder.S3Logs { return apiObject } -func flattenInstanceMetadataOptions(apiObject *imagebuilder.InstanceMetadataOptions) map[string]interface{} { +func flattenInstanceMetadataOptions(apiObject *awstypes.InstanceMetadataOptions) map[string]interface{} { if apiObject == nil { return nil } @@ -468,17 +467,17 @@ func flattenInstanceMetadataOptions(apiObject *imagebuilder.InstanceMetadataOpti tfMap := map[string]interface{}{} if v := apiObject.HttpPutResponseHopLimit; v != nil { - tfMap["http_put_response_hop_limit"] = aws.Int64Value(v) + tfMap["http_put_response_hop_limit"] = aws.ToInt32(v) } if v := apiObject.HttpTokens; v != nil { - tfMap["http_tokens"] = aws.StringValue(v) + tfMap["http_tokens"] = aws.ToString(v) } return tfMap } -func flattenLogging(apiObject *imagebuilder.Logging) map[string]interface{} { +func flattenLogging(apiObject *awstypes.Logging) map[string]interface{} { if apiObject == nil { return nil } @@ -492,7 +491,7 @@ func flattenLogging(apiObject *imagebuilder.Logging) map[string]interface{} { return tfMap } -func flattenS3Logs(apiObject *imagebuilder.S3Logs) map[string]interface{} { +func flattenS3Logs(apiObject *awstypes.S3Logs) map[string]interface{} { if apiObject == nil { return nil } @@ -500,11 +499,11 @@ func flattenS3Logs(apiObject *imagebuilder.S3Logs) map[string]interface{} { tfMap := map[string]interface{}{} if v := apiObject.S3BucketName; v != nil { - tfMap[names.AttrS3BucketName] = aws.StringValue(v) + tfMap[names.AttrS3BucketName] = aws.ToString(v) } if v := apiObject.S3KeyPrefix; v != nil { - tfMap[names.AttrS3KeyPrefix] = aws.StringValue(v) + tfMap[names.AttrS3KeyPrefix] = aws.ToString(v) } return tfMap diff --git a/internal/service/imagebuilder/infrastructure_configuration_data_source.go b/internal/service/imagebuilder/infrastructure_configuration_data_source.go index 54e16a81654..dbf00c3389f 100644 --- a/internal/service/imagebuilder/infrastructure_configuration_data_source.go +++ b/internal/service/imagebuilder/infrastructure_configuration_data_source.go @@ -6,8 +6,7 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -17,8 +16,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_infrastructure_configuration") -func DataSourceInfrastructureConfiguration() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_infrastructure_configuration", name="Infrastructure Configuration") +// @Tags +func dataSourceInfrastructureConfiguration() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceInfrastructureConfigurationRead, @@ -122,54 +122,45 @@ func DataSourceInfrastructureConfiguration() *schema.Resource { func dataSourceInfrastructureConfigurationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetInfrastructureConfigurationInput{} - - if v, ok := d.GetOk(names.AttrARN); ok { - input.InfrastructureConfigurationArn = aws.String(v.(string)) - } - - output, err := conn.GetInfrastructureConfigurationWithContext(ctx, input) + arn := d.Get(names.AttrARN).(string) + infrastructureConfiguration, err := findInfrastructureConfigurationByARN(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Infrastructure Configuration (%s): %s", d.Id(), err) - } - - if output == nil || output.InfrastructureConfiguration == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Infrastructure Configuration (%s): empty response", d.Id()) + return sdkdiag.AppendErrorf(diags, "reading Image Builder Infrastructure Configuration (%s): %s", arn, err) } - infrastructureConfiguration := output.InfrastructureConfiguration - - d.SetId(aws.StringValue(infrastructureConfiguration.Arn)) + d.SetId(aws.ToString(infrastructureConfiguration.Arn)) d.Set(names.AttrARN, infrastructureConfiguration.Arn) d.Set("date_created", infrastructureConfiguration.DateCreated) d.Set("date_updated", infrastructureConfiguration.DateUpdated) d.Set(names.AttrDescription, infrastructureConfiguration.Description) - if infrastructureConfiguration.InstanceMetadataOptions != nil { - d.Set("instance_metadata_options", []interface{}{flattenInstanceMetadataOptions(infrastructureConfiguration.InstanceMetadataOptions)}) + if err := d.Set("instance_metadata_options", []interface{}{flattenInstanceMetadataOptions(infrastructureConfiguration.InstanceMetadataOptions)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting instance_metadata_options: %s", err) + } } else { d.Set("instance_metadata_options", nil) } - d.Set("instance_profile_name", infrastructureConfiguration.InstanceProfileName) - d.Set("instance_types", aws.StringValueSlice(infrastructureConfiguration.InstanceTypes)) + d.Set("instance_types", infrastructureConfiguration.InstanceTypes) d.Set("key_pair", infrastructureConfiguration.KeyPair) if infrastructureConfiguration.Logging != nil { - d.Set("logging", []interface{}{flattenLogging(infrastructureConfiguration.Logging)}) + if err := d.Set("logging", []interface{}{flattenLogging(infrastructureConfiguration.Logging)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting logging: %s", err) + } } else { d.Set("logging", nil) } d.Set(names.AttrName, infrastructureConfiguration.Name) d.Set(names.AttrResourceTags, KeyValueTags(ctx, infrastructureConfiguration.ResourceTags).Map()) - d.Set(names.AttrSecurityGroupIDs, aws.StringValueSlice(infrastructureConfiguration.SecurityGroupIds)) + d.Set(names.AttrSecurityGroupIDs, infrastructureConfiguration.SecurityGroupIds) d.Set(names.AttrSNSTopicARN, infrastructureConfiguration.SnsTopicArn) d.Set(names.AttrSubnetID, infrastructureConfiguration.SubnetId) - d.Set(names.AttrTags, KeyValueTags(ctx, infrastructureConfiguration.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()) d.Set("terminate_instance_on_failure", infrastructureConfiguration.TerminateInstanceOnFailure) + setTagsOut(ctx, infrastructureConfiguration.Tags) + return diags } diff --git a/internal/service/imagebuilder/infrastructure_configuration_test.go b/internal/service/imagebuilder/infrastructure_configuration_test.go index 1f60191ddb7..c73f562e2a6 100644 --- a/internal/service/imagebuilder/infrastructure_configuration_test.go +++ b/internal/service/imagebuilder/infrastructure_configuration_test.go @@ -8,15 +8,13 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfimagebuilder "github.com/hashicorp/terraform-provider-aws/internal/service/imagebuilder" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -580,56 +578,42 @@ func TestAccImageBuilderInfrastructureConfiguration_terminateInstanceOnFailure(t func testAccCheckInfrastructureConfigurationDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_imagebuilder_infrastructure_configuration" { continue } - input := &imagebuilder.GetInfrastructureConfigurationInput{ - InfrastructureConfigurationArn: aws.String(rs.Primary.ID), - } - - output, err := conn.GetInfrastructureConfigurationWithContext(ctx, input) + _, err := tfimagebuilder.FindInfrastructureConfigurationByARN(ctx, conn, rs.Primary.ID) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfresource.NotFound(err) { continue } if err != nil { - return fmt.Errorf("error getting Image Builder Infrastructure Configuration (%s): %w", rs.Primary.ID, err) + return err } - if output != nil { - return fmt.Errorf("Image Builder Infrastructure Configuration (%s) still exists", rs.Primary.ID) - } + return fmt.Errorf("Image Builder Infrastructure Configuration %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckInfrastructureConfigurationExists(ctx context.Context, resourceName string) resource.TestCheckFunc { +func testAccCheckInfrastructureConfigurationExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("resource not found: %s", resourceName) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetInfrastructureConfigurationInput{ - InfrastructureConfigurationArn: aws.String(rs.Primary.ID), + return fmt.Errorf("Not found: %s", n) } - _, err := conn.GetInfrastructureConfigurationWithContext(ctx, input) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) - if err != nil { - return fmt.Errorf("error getting Image Builder Infrastructure Configuration (%s): %w", rs.Primary.ID, err) - } + _, err := tfimagebuilder.FindInfrastructureConfigurationByARN(ctx, conn, rs.Primary.ID) - return nil + return err } } diff --git a/internal/service/imagebuilder/infrastructure_configurations_data_source.go b/internal/service/imagebuilder/infrastructure_configurations_data_source.go index a729eeb563b..e009c2a4576 100644 --- a/internal/service/imagebuilder/infrastructure_configurations_data_source.go +++ b/internal/service/imagebuilder/infrastructure_configurations_data_source.go @@ -6,21 +6,24 @@ package imagebuilder import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters" - namevaluesfiltersv1 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v1" + namevaluesfiltersv2 "github.com/hashicorp/terraform-provider-aws/internal/namevaluesfilters/v2" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_imagebuilder_infrastructure_configurations") -func DataSourceInfrastructureConfigurations() *schema.Resource { +// @SDKDataSource("aws_imagebuilder_infrastructure_configurations", name="Infrastructure Configurations") +func dataSourceInfrastructureConfigurations() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceInfrastructureConfigurationsRead, + Schema: map[string]*schema.Schema{ names.AttrARNs: { Type: schema.TypeSet, @@ -39,46 +42,44 @@ func DataSourceInfrastructureConfigurations() *schema.Resource { func dataSourceInfrastructureConfigurationsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) input := &imagebuilder.ListInfrastructureConfigurationsInput{} if v, ok := d.GetOk(names.AttrFilter); ok { - input.Filters = namevaluesfiltersv1.New(v.(*schema.Set)).ImageBuilderFilters() + input.Filters = namevaluesfiltersv2.New(v.(*schema.Set)).ImageBuilderFilters() } - var results []*imagebuilder.InfrastructureConfigurationSummary + infrastructureConfigurations, err := findInfrastructureConfigurations(ctx, conn, input) - err := conn.ListInfrastructureConfigurationsPagesWithContext(ctx, input, func(page *imagebuilder.ListInfrastructureConfigurationsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Image Builder Infrastructure Configurations: %s", err) + } - for _, infrastructureConfigurationSummary := range page.InfrastructureConfigurationSummaryList { - if infrastructureConfigurationSummary == nil { - continue - } + d.SetId(meta.(*conns.AWSClient).Region) + d.Set(names.AttrARNs, tfslices.ApplyToAll(infrastructureConfigurations, func(v awstypes.InfrastructureConfigurationSummary) string { + return aws.ToString(v.Arn) + })) + d.Set(names.AttrNames, tfslices.ApplyToAll(infrastructureConfigurations, func(v awstypes.InfrastructureConfigurationSummary) string { + return aws.ToString(v.Name) + })) - results = append(results, infrastructureConfigurationSummary) - } + return diags +} - return !lastPage - }) +func findInfrastructureConfigurations(ctx context.Context, conn *imagebuilder.Client, input *imagebuilder.ListInfrastructureConfigurationsInput) ([]awstypes.InfrastructureConfigurationSummary, error) { + var output []awstypes.InfrastructureConfigurationSummary - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Image Builder Infrastructure Configurations: %s", err) - } + pages := imagebuilder.NewListInfrastructureConfigurationsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - var arns, nms []string + if err != nil { + return nil, err + } - for _, r := range results { - arns = append(arns, aws.StringValue(r.Arn)) - nms = append(nms, aws.StringValue(r.Name)) + output = append(output, page.InfrastructureConfigurationSummaryList...) } - d.SetId(meta.(*conns.AWSClient).Region) - d.Set(names.AttrARNs, arns) - d.Set(names.AttrNames, nms) - - return diags + return output, nil } diff --git a/internal/service/imagebuilder/lifecycle_policy.go b/internal/service/imagebuilder/lifecycle_policy.go new file mode 100644 index 00000000000..de77993cb4f --- /dev/null +++ b/internal/service/imagebuilder/lifecycle_policy.go @@ -0,0 +1,555 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package imagebuilder + +import ( + "context" + "fmt" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + sdkid "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource(name="Lifecycle Policy") +// @Tags(identifierAttribute="id") +func newLifecyclePolicyResource(_ context.Context) (resource.ResourceWithConfigure, error) { + return &lifecyclePolicyResource{}, nil +} + +type lifecyclePolicyResource struct { + framework.ResourceWithConfigure + framework.WithImportByID +} + +func (*lifecyclePolicyResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_imagebuilder_lifecycle_policy" +} + +func (r *lifecyclePolicyResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + lifecyclePolicyStatusType := fwtypes.StringEnumType[awstypes.LifecyclePolicyStatus]() + + response.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + names.AttrARN: framework.ARNAttributeComputedOnly(), + names.AttrDescription: schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 1024), + }, + }, + "execution_role": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Required: true, + }, + names.AttrID: framework.IDAttribute(), + names.AttrName: schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^[-_A-Za-z-0-9][-_A-Za-z0-9 ]{1,126}[-_A-Za-z-0-9]$`), ""), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + names.AttrResourceType: schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.LifecyclePolicyResourceType](), + Required: true, + }, + names.AttrStatus: schema.StringAttribute{ + CustomType: lifecyclePolicyStatusType, + Optional: true, + Computed: true, + Default: lifecyclePolicyStatusType.AttributeDefault(awstypes.LifecyclePolicyStatusEnabled), + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + names.AttrTags: tftags.TagsAttribute(), + names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), + }, + Blocks: map[string]schema.Block{ + "policy_detail": schema.SetNestedBlock{ + CustomType: fwtypes.NewSetNestedObjectTypeOf[lifecyclePolicyDetailModel](ctx), + Validators: []validator.Set{ + setvalidator.IsRequired(), + setvalidator.SizeBetween(1, 3), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + names.AttrAction: schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[lifecyclePolicyDetailActionModel](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + names.AttrType: schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.LifecyclePolicyDetailActionType](), + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "include_resources": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[lifecyclePolicyDetailActionIncludeResourcesModel](ctx), + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "amis": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + "containers": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + "snapshots": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + }, + }, + }, + }, + }, + }, + "exclusion_rules": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[lifecyclePolicyDetailExclusionRulesModel](ctx), + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "tag_map": schema.MapAttribute{ + CustomType: fwtypes.MapOfStringType, + Optional: true, + ElementType: types.StringType, + }, + }, + Blocks: map[string]schema.Block{ + "amis": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[lifecyclePolicyDetailExclusionRulesAMIsModel](ctx), + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "is_public": schema.BoolAttribute{ + Optional: true, + }, + "regions": schema.ListAttribute{ + CustomType: fwtypes.ListOfStringType, + ElementType: types.StringType, + Optional: true, + }, + "shared_accounts": schema.ListAttribute{ + CustomType: fwtypes.ListOfStringType, + ElementType: types.StringType, + Optional: true, + }, + "tag_map": schema.MapAttribute{ + CustomType: fwtypes.MapOfStringType, + Optional: true, + ElementType: types.StringType, + }, + }, + Blocks: map[string]schema.Block{ + "last_launched": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[lifecyclePolicyDetailExclusionRulesAmisLastLaunchedModel](ctx), + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + names.AttrUnit: schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.LifecyclePolicyTimeUnit](), + Required: true, + }, + names.AttrValue: schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.Between(1, 365), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + names.AttrFilter: schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[lifecyclePolicyDetailFilterModel](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "retain_at_least": schema.Int64Attribute{ + Optional: true, + Validators: []validator.Int64{ + int64validator.Between(1, 10), + }, + }, + names.AttrType: schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.LifecyclePolicyDetailFilterType](), + Required: true, + }, + names.AttrUnit: schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.LifecyclePolicyTimeUnit](), + Optional: true, + }, + names.AttrValue: schema.Int64Attribute{ + Required: true, + Validators: []validator.Int64{ + int64validator.Between(1, 1000), + }, + }, + }, + }, + }, + }, + }, + }, + "resource_selection": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[lifecyclePolicyResourceSelectionModel](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "tag_map": schema.MapAttribute{ + CustomType: fwtypes.MapOfStringType, + Optional: true, + ElementType: types.StringType, + }, + }, + Blocks: map[string]schema.Block{ + "recipe": schema.SetNestedBlock{ + CustomType: fwtypes.NewSetNestedObjectTypeOf[lifecyclePolicyResourceSelectionRecipeModel](ctx), + Validators: []validator.Set{ + setvalidator.SizeAtMost(50), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + names.AttrName: schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^[-_A-Za-z-0-9][-_A-Za-z0-9 ]{1,126}[-_A-Za-z-0-9]$`), ""), + }, + }, + "semantic_version": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+$`), ""), + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (r *lifecyclePolicyResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data lifecyclePolicyResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + conn := r.Meta().ImageBuilderClient(ctx) + + name := fwflex.StringValueFromFramework(ctx, data.Name) + input := &imagebuilder.CreateLifecyclePolicyInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, data, input)...) + if response.Diagnostics.HasError() { + return + } + + // Additional fields. + input.ClientToken = aws.String(sdkid.UniqueId()) + input.Tags = getTagsIn(ctx) + + outputRaw, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, func() (interface{}, error) { + return conn.CreateLifecyclePolicy(ctx, input) + }, errCodeInvalidParameterValueException, "The provided role does not exist or does not have sufficient permissions") + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("creating Image Builder Lifecycle Policy (%s)", name), err.Error()) + + return + } + + output := outputRaw.(*imagebuilder.CreateLifecyclePolicyOutput) + + // Set values for unknowns. + data.LifecyclePolicyARN = fwflex.StringToFramework(ctx, output.LifecyclePolicyArn) + data.setID() + + // Read to retrieve computed arguments not part of the create response. + policy, err := findLifecyclePolicyByARN(ctx, conn, data.ID.ValueString()) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("reading Image Builder Lifecycle Policy (%s)", data.ID.ValueString()), err.Error()) + + return + } + + response.Diagnostics.Append(fwflex.Flatten(ctx, policy, &data)...) + if response.Diagnostics.HasError() { + return + } + + response.Diagnostics.Append(response.State.Set(ctx, data)...) +} + +func (r *lifecyclePolicyResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data lifecyclePolicyResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + if err := data.InitFromID(); err != nil { + response.Diagnostics.AddError("parsing resource ID", err.Error()) + + return + } + + conn := r.Meta().ImageBuilderClient(ctx) + + policy, err := findLifecyclePolicyByARN(ctx, conn, data.ID.ValueString()) + + if tfresource.NotFound(err) { + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) + + return + } + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("reading Image Builder Lifecycle Policy (%s)", data.ID.ValueString()), err.Error()) + + return + } + + setTagsOut(ctx, policy.Tags) + + // Set attributes for import. + response.Diagnostics.Append(fwflex.Flatten(ctx, policy, &data)...) + if response.Diagnostics.HasError() { + return + } + + response.Diagnostics.Append(response.State.Set(ctx, &data)...) +} + +func (r *lifecyclePolicyResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var old, new lifecyclePolicyResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &new)...) + if response.Diagnostics.HasError() { + return + } + response.Diagnostics.Append(request.State.Get(ctx, &old)...) + if response.Diagnostics.HasError() { + return + } + + conn := r.Meta().ImageBuilderClient(ctx) + + if !new.Description.Equal(old.Description) || + !new.ExecutionRole.Equal(old.ExecutionRole) || + !new.PolicyDetails.Equal(old.PolicyDetails) || + !new.ResourceSelection.Equal(old.ResourceSelection) || + !new.ResourceType.Equal(old.ResourceType) || + !new.Status.Equal(old.Status) { + input := &imagebuilder.UpdateLifecyclePolicyInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) + if response.Diagnostics.HasError() { + return + } + + // Additional fields. + input.ClientToken = aws.String(sdkid.UniqueId()) + + _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, func() (interface{}, error) { + return conn.UpdateLifecyclePolicy(ctx, input) + }, errCodeInvalidParameterValueException, "The provided role does not exist or does not have sufficient permissions") + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("updating Image Builder Lifecycle Policy (%s)", new.ID.ValueString()), err.Error()) + + return + } + } + + response.Diagnostics.Append(response.State.Set(ctx, &new)...) +} + +func (r *lifecyclePolicyResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var data lifecyclePolicyResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + conn := r.Meta().ImageBuilderClient(ctx) + + _, err := conn.DeleteLifecyclePolicy(ctx, &imagebuilder.DeleteLifecyclePolicyInput{ + LifecyclePolicyArn: data.ID.ValueStringPointer(), + }) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return + } + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("deleting Image Builder Lifecycle Policy (%s)", data.ID.ValueString()), err.Error()) + + return + } +} + +func (r *lifecyclePolicyResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) +} + +func findLifecyclePolicyByARN(ctx context.Context, conn *imagebuilder.Client, arn string) (*awstypes.LifecyclePolicy, error) { + input := &imagebuilder.GetLifecyclePolicyInput{ + LifecyclePolicyArn: aws.String(arn), + } + + output, err := conn.GetLifecyclePolicy(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.LifecyclePolicy, nil +} + +type lifecyclePolicyResourceModel struct { + Description types.String `tfsdk:"description"` + ExecutionRole fwtypes.ARN `tfsdk:"execution_role"` + ID types.String `tfsdk:"id"` + LifecyclePolicyARN types.String `tfsdk:"arn"` + Name types.String `tfsdk:"name"` + PolicyDetails fwtypes.SetNestedObjectValueOf[lifecyclePolicyDetailModel] `tfsdk:"policy_detail"` + ResourceSelection fwtypes.ListNestedObjectValueOf[lifecyclePolicyResourceSelectionModel] `tfsdk:"resource_selection"` + ResourceType fwtypes.StringEnum[awstypes.LifecyclePolicyResourceType] `tfsdk:"resource_type"` + Status fwtypes.StringEnum[awstypes.LifecyclePolicyStatus] `tfsdk:"status"` + Tags tftags.Map `tfsdk:"tags"` + TagsAll tftags.Map `tfsdk:"tags_all"` +} + +func (model *lifecyclePolicyResourceModel) InitFromID() error { + model.LifecyclePolicyARN = model.ID + + return nil +} + +func (model *lifecyclePolicyResourceModel) setID() { + model.ID = model.LifecyclePolicyARN +} + +type lifecyclePolicyDetailModel struct { + Action fwtypes.ListNestedObjectValueOf[lifecyclePolicyDetailActionModel] `tfsdk:"action"` + ExclusionRules fwtypes.ListNestedObjectValueOf[lifecyclePolicyDetailExclusionRulesModel] `tfsdk:"exclusion_rules"` + Filter fwtypes.ListNestedObjectValueOf[lifecyclePolicyDetailFilterModel] `tfsdk:"filter"` +} + +type lifecyclePolicyDetailActionModel struct { + IncludeResources fwtypes.ListNestedObjectValueOf[lifecyclePolicyDetailActionIncludeResourcesModel] `tfsdk:"include_resources"` + Type fwtypes.StringEnum[awstypes.LifecyclePolicyDetailActionType] `tfsdk:"type"` +} + +type lifecyclePolicyDetailActionIncludeResourcesModel struct { + AMIs types.Bool `tfsdk:"amis"` + Containers types.Bool `tfsdk:"containers"` + Snapshots types.Bool `tfsdk:"snapshots"` +} + +type lifecyclePolicyDetailExclusionRulesModel struct { + AMIs fwtypes.ListNestedObjectValueOf[lifecyclePolicyDetailExclusionRulesAMIsModel] `tfsdk:"amis"` + TagMap fwtypes.MapValueOf[types.String] `tfsdk:"tag_map"` +} + +type lifecyclePolicyDetailExclusionRulesAMIsModel struct { + IsPublic types.Bool `tfsdk:"is_public"` + LastLaunched fwtypes.ListNestedObjectValueOf[lifecyclePolicyDetailExclusionRulesAmisLastLaunchedModel] `tfsdk:"last_launched"` + Regions fwtypes.ListValueOf[types.String] `tfsdk:"regions"` + SharedAccounts fwtypes.ListValueOf[types.String] `tfsdk:"shared_accounts"` + TagMap fwtypes.MapValueOf[types.String] `tfsdk:"tag_map"` +} + +type lifecyclePolicyDetailExclusionRulesAmisLastLaunchedModel struct { + Unit fwtypes.StringEnum[awstypes.LifecyclePolicyTimeUnit] `tfsdk:"unit"` + Value types.Int64 `tfsdk:"value"` +} + +type lifecyclePolicyDetailFilterModel struct { + RetainAtLeast types.Int64 `tfsdk:"retain_at_least"` + Type fwtypes.StringEnum[awstypes.LifecyclePolicyDetailFilterType] `tfsdk:"type"` + Unit fwtypes.StringEnum[awstypes.LifecyclePolicyTimeUnit] `tfsdk:"unit"` + Value types.Int64 `tfsdk:"value"` +} + +type lifecyclePolicyResourceSelectionModel struct { + Recipes fwtypes.SetNestedObjectValueOf[lifecyclePolicyResourceSelectionRecipeModel] `tfsdk:"recipe"` + TagMap fwtypes.MapValueOf[types.String] `tfsdk:"tag_map"` +} + +type lifecyclePolicyResourceSelectionRecipeModel struct { + Name types.String `tfsdk:"name"` + SemanticVersion types.String `tfsdk:"semantic_version"` +} diff --git a/internal/service/imagebuilder/lifecycle_policy_test.go b/internal/service/imagebuilder/lifecycle_policy_test.go new file mode 100644 index 00000000000..72eb6bf1ff4 --- /dev/null +++ b/internal/service/imagebuilder/lifecycle_policy_test.go @@ -0,0 +1,602 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package imagebuilder_test + +import ( + "context" + "fmt" + "testing" + + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfimagebuilder "github.com/hashicorp/terraform-provider-aws/internal/service/imagebuilder" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccImageBuilderLifecyclePolicy_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_imagebuilder_lifecycle_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ImageBuilderServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckLifecyclePolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccLifecyclePolicyConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLifecyclePolicyExists(ctx, resourceName), + acctest.CheckResourceAttrRegionalARN(resourceName, names.AttrARN, "imagebuilder", fmt.Sprintf("lifecycle-policy/%s", rName)), + resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "Used for setting lifecycle policies"), + resource.TestCheckResourceAttrSet(resourceName, "execution_role"), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + resource.TestCheckResourceAttr(resourceName, "policy_detail.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.0.type", string(awstypes.LifecyclePolicyDetailActionTypeDelete)), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.type", string(awstypes.LifecyclePolicyDetailFilterTypeAge)), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.value", "6"), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.retain_at_least", acctest.Ct10), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.unit", string(awstypes.LifecyclePolicyTimeUnitYears)), + resource.TestCheckResourceAttr(resourceName, "resource_selection.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "resource_selection.0.tag_map.%", acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, "resource_selection.0.tag_map.key1", acctest.CtValue1), + resource.TestCheckResourceAttr(resourceName, "resource_selection.0.tag_map.key2", acctest.CtValue2), + resource.TestCheckResourceAttr(resourceName, names.AttrResourceType, string(awstypes.LifecyclePolicyResourceTypeAmiImage)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccImageBuilderLifecyclePolicy_policyDetails(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_imagebuilder_lifecycle_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ImageBuilderServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckLifecyclePolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccLifecyclePolicyConfig_policyDetails(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLifecyclePolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "policy_detail.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.0.type", string(awstypes.LifecyclePolicyDetailActionTypeDisable)), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.0.include_resources.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.0.include_resources.0.amis", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.is_public", acctest.CtFalse), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.last_launched.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.last_launched.0.unit", string(awstypes.LifecyclePolicyTimeUnitDays)), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.last_launched.0.value", "7"), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.tag_map.%", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.tag_map.key1", acctest.CtValue1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.type", string(awstypes.LifecyclePolicyDetailFilterTypeAge)), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.value", "6"), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.retain_at_least", "5"), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.unit", string(awstypes.LifecyclePolicyTimeUnitYears)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccLifecyclePolicyConfig_policyDetailsUpdated(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLifecyclePolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "policy_detail.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.0.type", string(awstypes.LifecyclePolicyDetailActionTypeDelete)), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.0.include_resources.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.0.include_resources.0.amis", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.action.0.include_resources.0.snapshots", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.is_public", acctest.CtTrue), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.regions.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.last_launched.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.last_launched.0.unit", string(awstypes.LifecyclePolicyTimeUnitWeeks)), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.last_launched.0.value", acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.tag_map.%", acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.tag_map.key1", acctest.CtValue1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.exclusion_rules.0.amis.0.tag_map.key2", acctest.CtValue2), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.type", string(awstypes.LifecyclePolicyDetailFilterTypeCount)), + resource.TestCheckResourceAttr(resourceName, "policy_detail.0.filter.0.value", acctest.Ct10), + ), + }, + }, + }) +} + +func TestAccImageBuilderLifecyclePolicy_resourceSelection(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_imagebuilder_lifecycle_policy.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ImageBuilderServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckLifecyclePolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccLifecyclePolicyConfig_resourceSelection(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLifecyclePolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "resource_selection.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "resource_selection.0.recipe.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "resource_selection.0.recipe.0.name", rName), + resource.TestCheckResourceAttr(resourceName, "resource_selection.0.recipe.0.semantic_version", "1.0.0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccLifecyclePolicyConfig_resourceSelectionUpdated(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckLifecyclePolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "resource_selection.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "resource_selection.0.recipe.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "resource_selection.0.recipe.0.name", rName), + resource.TestCheckResourceAttr(resourceName, "resource_selection.0.recipe.0.semantic_version", "2.0.0"), + ), + }, + }, + }) +} + +func TestAccImageBuilderLifecyclePolicy_tags(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_imagebuilder_lifecycle_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ImageBuilderServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckLifecyclePolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccLifecyclePolicyConfig_tags1(rName, acctest.CtKey1, acctest.CtValue1), + Check: resource.ComposeTestCheckFunc( + testAccCheckLifecyclePolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccLifecyclePolicyConfig_tags2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckLifecyclePolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), + ), + }, + { + Config: testAccLifecyclePolicyConfig_tags1(rName, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckLifecyclePolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), + ), + }, + }, + }) +} + +func TestAccImageBuilderLifecyclePolicy_disappears(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_imagebuilder_lifecycle_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ImageBuilderServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckLifecyclePolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccLifecyclePolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckLifecyclePolicyExists(ctx, resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfimagebuilder.ResourceLifecyclePolicy, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckLifecyclePolicyExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) + + _, err := tfimagebuilder.FindLifecyclePolicyByARN(ctx, conn, rs.Primary.ID) + + return err + } +} + +func testAccCheckLifecyclePolicyDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_imagebuilder_lifecycle_policy" { + continue + } + + _, err := tfimagebuilder.FindLifecyclePolicyByARN(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("Image Builder Lifecycle Policy %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccLifecyclePolicyConfig_base(rName string) string { + return fmt.Sprintf(` +data "aws_region" "current" {} + +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "imagebuilder.${data.aws_partition.current.dns_suffix}" + } + }] + }) + name = %[1]q +} + +resource "aws_iam_role_policy_attachment" "test" { + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/EC2ImageBuilderLifecycleExecutionPolicy" + role = aws_iam_role.test.name +} +`, rName) +} + +func testAccLifecyclePolicyConfig_baseComponent(rName string) string { + return fmt.Sprintf(` +resource "aws_imagebuilder_component" "test" { + data = yamlencode({ + phases = [{ + name = "build" + steps = [{ + action = "ExecuteBash" + inputs = { + commands = ["echo 'hello world'"] + } + name = "example" + onFailure = "Continue" + }] + }] + schemaVersion = 1.0 + }) + name = %[1]q + platform = "Linux" + version = "1.0.0" +} + `, rName) +} + +func testAccLifecyclePolicyConfig_basic(rName string) string { + return acctest.ConfigCompose(testAccLifecyclePolicyConfig_base(rName), fmt.Sprintf(` +resource "aws_imagebuilder_lifecycle_policy" "test" { + name = %[1]q + description = "Used for setting lifecycle policies" + execution_role = aws_iam_role.test.arn + resource_type = "AMI_IMAGE" + policy_detail { + action { + type = "DELETE" + } + filter { + type = "AGE" + value = 6 + retain_at_least = 10 + unit = "YEARS" + } + } + resource_selection { + tag_map = { + "key1" = "value1" + "key2" = "value2" + } + } + + depends_on = [aws_iam_role_policy_attachment.test] +} +`, rName)) +} + +func testAccLifecyclePolicyConfig_policyDetails(rName string) string { + return acctest.ConfigCompose(testAccLifecyclePolicyConfig_base(rName), fmt.Sprintf(` +resource "aws_imagebuilder_lifecycle_policy" "test" { + name = %[1]q + description = "Used for setting lifecycle policies" + execution_role = aws_iam_role.test.arn + resource_type = "AMI_IMAGE" + policy_detail { + action { + type = "DISABLE" + include_resources { + amis = true + } + } + exclusion_rules { + amis { + is_public = false + last_launched { + unit = "DAYS" + value = 7 + } + tag_map = { + "key1" = "value1" + } + } + } + filter { + type = "AGE" + value = "6" + retain_at_least = "5" + unit = "YEARS" + } + } + resource_selection { + tag_map = { + "key1" = "value1" + "key2" = "value2" + } + } + + depends_on = [aws_iam_role_policy_attachment.test] +} +`, rName)) +} + +func testAccLifecyclePolicyConfig_policyDetailsUpdated(rName string) string { + return acctest.ConfigCompose(testAccLifecyclePolicyConfig_base(rName), fmt.Sprintf(` +resource "aws_imagebuilder_lifecycle_policy" "test" { + name = %[1]q + description = "Used for setting lifecycle policies" + execution_role = aws_iam_role.test.arn + resource_type = "AMI_IMAGE" + policy_detail { + action { + type = "DELETE" + include_resources { + amis = true + snapshots = true + } + } + exclusion_rules { + amis { + is_public = true + regions = [data.aws_region.current.name] + last_launched { + unit = "WEEKS" + value = 2 + } + tag_map = { + "key1" = "value1" + "key2" = "value2" + } + } + } + filter { + type = "COUNT" + value = "10" + } + } + resource_selection { + tag_map = { + "key1" = "value1" + "key2" = "value2" + } + } + + depends_on = [aws_iam_role_policy_attachment.test] +} +`, rName)) +} + +func testAccLifecyclePolicyConfig_resourceSelection(rName string) string { + return acctest.ConfigCompose( + testAccLifecyclePolicyConfig_base(rName), + testAccLifecyclePolicyConfig_baseComponent(rName), + fmt.Sprintf(` +resource "aws_imagebuilder_image_recipe" "test" { + component { + component_arn = aws_imagebuilder_component.test.arn + } + + name = %[1]q + parent_image = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:image/amazon-linux-2-x86/x.x.x" + version = "1.0.0" +} + +resource "aws_imagebuilder_lifecycle_policy" "test" { + name = %[1]q + description = "Used for setting lifecycle policies" + execution_role = aws_iam_role.test.arn + resource_type = "AMI_IMAGE" + policy_detail { + action { + type = "DELETE" + } + filter { + type = "AGE" + value = 6 + retain_at_least = 10 + unit = "YEARS" + } + } + resource_selection { + recipe { + name = aws_imagebuilder_image_recipe.test.name + semantic_version = aws_imagebuilder_image_recipe.test.version + } + } + + depends_on = [aws_iam_role_policy_attachment.test] +} +`, rName)) +} + +func testAccLifecyclePolicyConfig_resourceSelectionUpdated(rName string) string { + return acctest.ConfigCompose( + testAccLifecyclePolicyConfig_base(rName), + testAccLifecyclePolicyConfig_baseComponent(rName), + fmt.Sprintf(` +resource "aws_imagebuilder_image_recipe" "test" { + component { + component_arn = aws_imagebuilder_component.test.arn + } + + name = %[1]q + parent_image = "arn:${data.aws_partition.current.partition}:imagebuilder:${data.aws_region.current.name}:aws:image/amazon-linux-2-x86/x.x.x" + version = "2.0.0" +} + +resource "aws_imagebuilder_lifecycle_policy" "test" { + name = %[1]q + description = "Used for setting lifecycle policies" + execution_role = aws_iam_role.test.arn + resource_type = "AMI_IMAGE" + policy_detail { + action { + type = "DELETE" + } + filter { + type = "AGE" + value = 6 + retain_at_least = 10 + unit = "YEARS" + } + } + resource_selection { + recipe { + name = aws_imagebuilder_image_recipe.test.name + semantic_version = aws_imagebuilder_image_recipe.test.version + } + } + + depends_on = [aws_iam_role_policy_attachment.test] +} +`, rName)) +} + +func testAccLifecyclePolicyConfig_tags1(rName string, tagKey1 string, tagValue1 string) string { + return acctest.ConfigCompose(testAccLifecyclePolicyConfig_base(rName), fmt.Sprintf(` +resource "aws_imagebuilder_lifecycle_policy" "test" { + name = %[1]q + execution_role = aws_iam_role.test.arn + resource_type = "AMI_IMAGE" + policy_detail { + action { + type = "DELETE" + } + filter { + type = "AGE" + value = 6 + unit = "YEARS" + } + } + resource_selection { + tag_map = { + "key" = "value" + } + } + + tags = { + %[2]q = %[3]q + } + + depends_on = [aws_iam_role_policy_attachment.test] +} +`, rName, tagKey1, tagValue1)) +} + +func testAccLifecyclePolicyConfig_tags2(rName string, tagKey1 string, tagValue1 string, tagKey2 string, tagValue2 string) string { + return acctest.ConfigCompose(testAccLifecyclePolicyConfig_base(rName), fmt.Sprintf(` +resource "aws_imagebuilder_lifecycle_policy" "test" { + name = %[1]q + execution_role = aws_iam_role.test.arn + resource_type = "AMI_IMAGE" + policy_detail { + action { + type = "DELETE" + } + filter { + type = "AGE" + value = 6 + unit = "YEARS" + } + } + resource_selection { + tag_map = { + "key" = "value" + } + } + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } + + depends_on = [aws_iam_role_policy_attachment.test] +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) +} diff --git a/internal/service/imagebuilder/service_endpoint_resolver_gen.go b/internal/service/imagebuilder/service_endpoint_resolver_gen.go index 993463ab654..21a9dff1235 100644 --- a/internal/service/imagebuilder/service_endpoint_resolver_gen.go +++ b/internal/service/imagebuilder/service_endpoint_resolver_gen.go @@ -6,65 +6,63 @@ import ( "context" "fmt" "net" - "net/url" - endpoints_sdkv1 "github.com/aws/aws-sdk-go/aws/endpoints" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + imagebuilder_sdkv2 "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + smithyendpoints "github.com/aws/smithy-go/endpoints" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-provider-aws/internal/errs" ) -var _ endpoints_sdkv1.Resolver = resolverSDKv1{} +var _ imagebuilder_sdkv2.EndpointResolverV2 = resolverSDKv2{} -type resolverSDKv1 struct { - ctx context.Context +type resolverSDKv2 struct { + defaultResolver imagebuilder_sdkv2.EndpointResolverV2 } -func newEndpointResolverSDKv1(ctx context.Context) resolverSDKv1 { - return resolverSDKv1{ - ctx: ctx, +func newEndpointResolverSDKv2() resolverSDKv2 { + return resolverSDKv2{ + defaultResolver: imagebuilder_sdkv2.NewDefaultEndpointResolverV2(), } } -func (r resolverSDKv1) EndpointFor(service, region string, opts ...func(*endpoints_sdkv1.Options)) (endpoint endpoints_sdkv1.ResolvedEndpoint, err error) { - ctx := r.ctx +func (r resolverSDKv2) ResolveEndpoint(ctx context.Context, params imagebuilder_sdkv2.EndpointParameters) (endpoint smithyendpoints.Endpoint, err error) { + params = params.WithDefaults() + useFIPS := aws_sdkv2.ToBool(params.UseFIPS) - var opt endpoints_sdkv1.Options - opt.Set(opts...) - - useFIPS := opt.UseFIPSEndpoint == endpoints_sdkv1.FIPSEndpointStateEnabled + if eps := params.Endpoint; aws_sdkv2.ToString(eps) != "" { + tflog.Debug(ctx, "setting endpoint", map[string]any{ + "tf_aws.endpoint": endpoint, + }) - defaultResolver := endpoints_sdkv1.DefaultResolver() + if useFIPS { + tflog.Debug(ctx, "endpoint set, ignoring UseFIPSEndpoint setting") + params.UseFIPS = aws_sdkv2.Bool(false) + } - if useFIPS { + return r.defaultResolver.ResolveEndpoint(ctx, params) + } else if useFIPS { ctx = tflog.SetField(ctx, "tf_aws.use_fips", useFIPS) - endpoint, err = defaultResolver.EndpointFor(service, region, opts...) + endpoint, err = r.defaultResolver.ResolveEndpoint(ctx, params) if err != nil { return endpoint, err } tflog.Debug(ctx, "endpoint resolved", map[string]any{ - "tf_aws.endpoint": endpoint.URL, + "tf_aws.endpoint": endpoint.URI.String(), }) - var endpointURL *url.URL - endpointURL, err = url.Parse(endpoint.URL) - if err != nil { - return endpoint, err - } - - hostname := endpointURL.Hostname() + hostname := endpoint.URI.Hostname() _, err = net.LookupHost(hostname) if err != nil { if dnsErr, ok := errs.As[*net.DNSError](err); ok && dnsErr.IsNotFound { tflog.Debug(ctx, "default endpoint host not found, disabling FIPS", map[string]any{ "tf_aws.hostname": hostname, }) - opts = append(opts, func(o *endpoints_sdkv1.Options) { - o.UseFIPSEndpoint = endpoints_sdkv1.FIPSEndpointStateDisabled - }) + params.UseFIPS = aws_sdkv2.Bool(false) } else { - err = fmt.Errorf("looking up accessanalyzer endpoint %q: %s", hostname, err) + err = fmt.Errorf("looking up imagebuilder endpoint %q: %s", hostname, err) return } } else { @@ -72,5 +70,13 @@ func (r resolverSDKv1) EndpointFor(service, region string, opts ...func(*endpoin } } - return defaultResolver.EndpointFor(service, region, opts...) + return r.defaultResolver.ResolveEndpoint(ctx, params) +} + +func withBaseEndpoint(endpoint string) func(*imagebuilder_sdkv2.Options) { + return func(o *imagebuilder_sdkv2.Options) { + if endpoint != "" { + o.BaseEndpoint = aws_sdkv2.String(endpoint) + } + } } diff --git a/internal/service/imagebuilder/service_endpoints_gen_test.go b/internal/service/imagebuilder/service_endpoints_gen_test.go index 97a094cd09f..88631c06a7c 100644 --- a/internal/service/imagebuilder/service_endpoints_gen_test.go +++ b/internal/service/imagebuilder/service_endpoints_gen_test.go @@ -4,18 +4,22 @@ package imagebuilder_test import ( "context" + "errors" "fmt" "maps" "net" "net/url" "os" "path/filepath" + "reflect" "strings" "testing" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/endpoints" - imagebuilder_sdkv1 "github.com/aws/aws-sdk-go/service/imagebuilder" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + imagebuilder_sdkv2 "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" "github.com/google/go-cmp/cmp" "github.com/hashicorp/aws-sdk-go-base/v2/servicemocks" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -238,57 +242,63 @@ func TestEndpointConfiguration(t *testing.T) { //nolint:paralleltest // uses t.S } func defaultEndpoint(region string) (url.URL, error) { - r := endpoints.DefaultResolver() + r := imagebuilder_sdkv2.NewDefaultEndpointResolverV2() - ep, err := r.EndpointFor(imagebuilder_sdkv1.EndpointsID, region, func(opt *endpoints.Options) { - opt.ResolveUnknownService = true + ep, err := r.ResolveEndpoint(context.Background(), imagebuilder_sdkv2.EndpointParameters{ + Region: aws_sdkv2.String(region), }) if err != nil { return url.URL{}, err } - url, _ := url.Parse(ep.URL) - - if url.Path == "" { - url.Path = "/" + if ep.URI.Path == "" { + ep.URI.Path = "/" } - return *url, nil + return ep.URI, nil } func defaultFIPSEndpoint(region string) (url.URL, error) { - r := endpoints.DefaultResolver() + r := imagebuilder_sdkv2.NewDefaultEndpointResolverV2() - ep, err := r.EndpointFor(imagebuilder_sdkv1.EndpointsID, region, func(opt *endpoints.Options) { - opt.ResolveUnknownService = true - opt.UseFIPSEndpoint = endpoints.FIPSEndpointStateEnabled + ep, err := r.ResolveEndpoint(context.Background(), imagebuilder_sdkv2.EndpointParameters{ + Region: aws_sdkv2.String(region), + UseFIPS: aws_sdkv2.Bool(true), }) if err != nil { return url.URL{}, err } - url, _ := url.Parse(ep.URL) - - if url.Path == "" { - url.Path = "/" + if ep.URI.Path == "" { + ep.URI.Path = "/" } - return *url, nil + return ep.URI, nil } func callService(ctx context.Context, t *testing.T, meta *conns.AWSClient) apiCallParams { t.Helper() - client := meta.ImageBuilderConn(ctx) - - req, _ := client.ListImagesRequest(&imagebuilder_sdkv1.ListImagesInput{}) + client := meta.ImageBuilderClient(ctx) - req.HTTPRequest.URL.Path = "/" + var result apiCallParams - return apiCallParams{ - endpoint: req.HTTPRequest.URL.String(), - region: aws_sdkv1.StringValue(client.Config.Region), + _, err := client.ListImages(ctx, &imagebuilder_sdkv2.ListImagesInput{}, + func(opts *imagebuilder_sdkv2.Options) { + opts.APIOptions = append(opts.APIOptions, + addRetrieveEndpointURLMiddleware(t, &result.endpoint), + addRetrieveRegionMiddleware(&result.region), + addCancelRequestMiddleware(), + ) + }, + ) + if err == nil { + t.Fatal("Expected an error, got none") + } else if !errors.Is(err, errCancelOperation) { + t.Fatalf("Unexpected error: %s", err) } + + return result } func withNoConfig(_ *caseSetup) { @@ -467,6 +477,89 @@ func testEndpointCase(t *testing.T, region string, testcase endpointTestCase, ca } } +func addRetrieveEndpointURLMiddleware(t *testing.T, endpoint *string) func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Finalize.Add( + retrieveEndpointURLMiddleware(t, endpoint), + middleware.After, + ) + } +} + +func retrieveEndpointURLMiddleware(t *testing.T, endpoint *string) middleware.FinalizeMiddleware { + return middleware.FinalizeMiddlewareFunc( + "Test: Retrieve Endpoint", + func(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) { + t.Helper() + + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + t.Fatalf("Expected *github.com/aws/smithy-go/transport/http.Request, got %s", fullTypeName(in.Request)) + } + + url := request.URL + url.RawQuery = "" + url.Path = "/" + + *endpoint = url.String() + + return next.HandleFinalize(ctx, in) + }) +} + +func addRetrieveRegionMiddleware(region *string) func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Serialize.Add( + retrieveRegionMiddleware(region), + middleware.After, + ) + } +} + +func retrieveRegionMiddleware(region *string) middleware.SerializeMiddleware { + return middleware.SerializeMiddlewareFunc( + "Test: Retrieve Region", + func(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) (middleware.SerializeOutput, middleware.Metadata, error) { + *region = awsmiddleware.GetRegion(ctx) + + return next.HandleSerialize(ctx, in) + }, + ) +} + +var errCancelOperation = fmt.Errorf("Test: Canceling request") + +func addCancelRequestMiddleware() func(*middleware.Stack) error { + return func(stack *middleware.Stack) error { + return stack.Finalize.Add( + cancelRequestMiddleware(), + middleware.After, + ) + } +} + +// cancelRequestMiddleware creates a Smithy middleware that intercepts the request before sending and cancels it +func cancelRequestMiddleware() middleware.FinalizeMiddleware { + return middleware.FinalizeMiddlewareFunc( + "Test: Cancel Requests", + func(_ context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (middleware.FinalizeOutput, middleware.Metadata, error) { + return middleware.FinalizeOutput{}, middleware.Metadata{}, errCancelOperation + }) +} + +func fullTypeName(i interface{}) string { + return fullValueTypeName(reflect.ValueOf(i)) +} + +func fullValueTypeName(v reflect.Value) string { + if v.Kind() == reflect.Ptr { + return "*" + fullValueTypeName(reflect.Indirect(v)) + } + + requestType := v.Type() + return fmt.Sprintf("%s.%s", requestType.PkgPath(), requestType.Name()) +} + func generateSharedConfigFile(config configFile) string { var buf strings.Builder diff --git a/internal/service/imagebuilder/service_package_gen.go b/internal/service/imagebuilder/service_package_gen.go index fde42af5d3c..769cc57c474 100644 --- a/internal/service/imagebuilder/service_package_gen.go +++ b/internal/service/imagebuilder/service_package_gen.go @@ -5,10 +5,8 @@ package imagebuilder import ( "context" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - session_sdkv1 "github.com/aws/aws-sdk-go/aws/session" - imagebuilder_sdkv1 "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/terraform-plugin-log/tflog" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + imagebuilder_sdkv2 "github.com/aws/aws-sdk-go-v2/service/imagebuilder" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/types" "github.com/hashicorp/terraform-provider-aws/names" @@ -21,65 +19,90 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv } func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { - return []*types.ServicePackageFrameworkResource{} + return []*types.ServicePackageFrameworkResource{ + { + Factory: newLifecyclePolicyResource, + Name: "Lifecycle Policy", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: names.AttrID, + }, + }, + } } func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { return []*types.ServicePackageSDKDataSource{ { - Factory: DataSourceComponent, + Factory: dataSourceComponent, TypeName: "aws_imagebuilder_component", + Name: "Component", + Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceComponents, + Factory: dataSourceComponents, TypeName: "aws_imagebuilder_components", Name: "Components", }, { - Factory: DataSourceContainerRecipe, + Factory: dataSourceContainerRecipe, TypeName: "aws_imagebuilder_container_recipe", + Name: "Container Recipe", + Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceContainerRecipes, + Factory: dataSourceContainerRecipes, TypeName: "aws_imagebuilder_container_recipes", Name: "Container Recipes", }, { - Factory: DataSourceDistributionConfiguration, + Factory: dataSourceDistributionConfiguration, TypeName: "aws_imagebuilder_distribution_configuration", + Name: "Distribution Configuration", + Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceDistributionConfigurations, + Factory: dataSourceDistributionConfigurations, TypeName: "aws_imagebuilder_distribution_configurations", + Name: "Distribution Configurations", }, { - Factory: DataSourceImage, + Factory: dataSourceImage, TypeName: "aws_imagebuilder_image", + Name: "Image", + Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceImagePipeline, + Factory: dataSourceImagePipeline, TypeName: "aws_imagebuilder_image_pipeline", + Name: "Image Pipeline", + Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceImagePipelines, + Factory: dataSourceImagePipelines, TypeName: "aws_imagebuilder_image_pipelines", + Name: "Image Pipelines", }, { - Factory: DataSourceImageRecipe, + Factory: dataSourceImageRecipe, TypeName: "aws_imagebuilder_image_recipe", + Name: "Image Recipe", + Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceImageRecipes, + Factory: dataSourceImageRecipes, TypeName: "aws_imagebuilder_image_recipes", Name: "Image Recipes", }, { - Factory: DataSourceInfrastructureConfiguration, + Factory: dataSourceInfrastructureConfiguration, TypeName: "aws_imagebuilder_infrastructure_configuration", + Name: "Infrastructure Configuration", + Tags: &types.ServicePackageResourceTags{}, }, { - Factory: DataSourceInfrastructureConfigurations, + Factory: dataSourceInfrastructureConfigurations, TypeName: "aws_imagebuilder_infrastructure_configurations", + Name: "Infrastructure Configurations", }, } } @@ -87,7 +110,7 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource { return []*types.ServicePackageSDKResource{ { - Factory: ResourceComponent, + Factory: resourceComponent, TypeName: "aws_imagebuilder_component", Name: "Component", Tags: &types.ServicePackageResourceTags{ @@ -95,7 +118,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceContainerRecipe, + Factory: resourceContainerRecipe, TypeName: "aws_imagebuilder_container_recipe", Name: "Container Recipe", Tags: &types.ServicePackageResourceTags{ @@ -103,7 +126,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceDistributionConfiguration, + Factory: resourceDistributionConfiguration, TypeName: "aws_imagebuilder_distribution_configuration", Name: "Distribution Configuration", Tags: &types.ServicePackageResourceTags{ @@ -111,7 +134,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceImage, + Factory: resourceImage, TypeName: "aws_imagebuilder_image", Name: "Image", Tags: &types.ServicePackageResourceTags{ @@ -119,7 +142,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceImagePipeline, + Factory: resourceImagePipeline, TypeName: "aws_imagebuilder_image_pipeline", Name: "Image Pipeline", Tags: &types.ServicePackageResourceTags{ @@ -127,7 +150,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceImageRecipe, + Factory: resourceImageRecipe, TypeName: "aws_imagebuilder_image_recipe", Name: "Image Recipe", Tags: &types.ServicePackageResourceTags{ @@ -135,7 +158,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceInfrastructureConfiguration, + Factory: resourceInfrastructureConfiguration, TypeName: "aws_imagebuilder_infrastructure_configuration", Name: "Infrastructure Configuration", Tags: &types.ServicePackageResourceTags{ @@ -143,7 +166,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka }, }, { - Factory: ResourceWorkflow, + Factory: resourceWorkflow, TypeName: "aws_imagebuilder_workflow", Name: "Workflow", Tags: &types.ServicePackageResourceTags{ @@ -157,22 +180,14 @@ func (p *servicePackage) ServicePackageName() string { return names.ImageBuilder } -// NewConn returns a new AWS SDK for Go v1 client for this service package's AWS API. -func (p *servicePackage) NewConn(ctx context.Context, config map[string]any) (*imagebuilder_sdkv1.Imagebuilder, error) { - sess := config[names.AttrSession].(*session_sdkv1.Session) - - cfg := aws_sdkv1.Config{} - - if endpoint := config[names.AttrEndpoint].(string); endpoint != "" { - tflog.Debug(ctx, "setting endpoint", map[string]any{ - "tf_aws.endpoint": endpoint, - }) - cfg.Endpoint = aws_sdkv1.String(endpoint) - } else { - cfg.EndpointResolver = newEndpointResolverSDKv1(ctx) - } +// NewClient returns a new AWS SDK for Go v2 client for this service package's AWS API. +func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*imagebuilder_sdkv2.Client, error) { + cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) - return imagebuilder_sdkv1.New(sess.Copy(&cfg)), nil + return imagebuilder_sdkv2.NewFromConfig(cfg, + imagebuilder_sdkv2.WithEndpointResolverV2(newEndpointResolverSDKv2()), + withBaseEndpoint(config[names.AttrEndpoint].(string)), + ), nil } func ServicePackage(ctx context.Context) conns.ServicePackage { diff --git a/internal/service/imagebuilder/status.go b/internal/service/imagebuilder/status.go deleted file mode 100644 index 225838f1025..00000000000 --- a/internal/service/imagebuilder/status.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package imagebuilder - -import ( - "context" - "fmt" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" -) - -// statusImage fetches the Image and its Status -func statusImage(ctx context.Context, conn *imagebuilder.Imagebuilder, imageBuildVersionArn string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - input := &imagebuilder.GetImageInput{ - ImageBuildVersionArn: aws.String(imageBuildVersionArn), - } - - output, err := conn.GetImageWithContext(ctx, input) - - if err != nil { - return nil, imagebuilder.ImageStatusPending, err - } - - if output == nil || output.Image == nil || output.Image.State == nil { - return nil, imagebuilder.ImageStatusPending, nil - } - - status := aws.StringValue(output.Image.State.Status) - - if status == imagebuilder.ImageStatusFailed { - return output.Image, status, fmt.Errorf("%s", aws.StringValue(output.Image.State.Reason)) - } - - return output.Image, status, nil - } -} diff --git a/internal/service/imagebuilder/sweep.go b/internal/service/imagebuilder/sweep.go index 76ce60ad7e4..2c3ac2906db 100644 --- a/internal/service/imagebuilder/sweep.go +++ b/internal/service/imagebuilder/sweep.go @@ -7,12 +7,14 @@ import ( "fmt" "log" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/go-multierror" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/sweep" - "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv1" + "github.com/hashicorp/terraform-provider-aws/internal/sweep/awsv2" + "github.com/hashicorp/terraform-provider-aws/internal/sweep/framework" + "github.com/hashicorp/terraform-provider-aws/names" ) func RegisterSweepers() { @@ -50,6 +52,11 @@ func RegisterSweepers() { Name: "aws_imagebuilder_infrastructure_configuration", F: sweepInfrastructureConfigurations, }) + + resource.AddTestSweepers("aws_imagebuilder_lifecycle_policy", &resource.Sweeper{ + Name: "aws_imagebuilder_lifecycle_policy", + F: sweepLifecyclePolicies, + }) } func sweepComponents(region string) error { @@ -58,75 +65,41 @@ func sweepComponents(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.ImageBuilderConn(ctx) - - sweepResources := make([]sweep.Sweepable, 0) - var sweeperErrs *multierror.Error - + conn := client.ImageBuilderClient(ctx) input := &imagebuilder.ListComponentsInput{ - Owner: aws.String(imagebuilder.OwnershipSelf), + Owner: types.OwnershipSelf, } - err = conn.ListComponentsPagesWithContext(ctx, input, func(page *imagebuilder.ListComponentsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - for _, componentVersion := range page.ComponentVersionList { - if componentVersion == nil { - continue - } - - arn := aws.StringValue(componentVersion.Arn) - input := &imagebuilder.ListComponentBuildVersionsInput{ - ComponentVersionArn: componentVersion.Arn, - } - - err := conn.ListComponentBuildVersionsPagesWithContext(ctx, input, func(page *imagebuilder.ListComponentBuildVersionsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } - - for _, componentSummary := range page.ComponentSummaryList { - if componentSummary == nil { - continue - } + sweepResources := make([]sweep.Sweepable, 0) - arn := aws.StringValue(componentSummary.Arn) + pages := imagebuilder.NewListComponentsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - r := ResourceComponent() - d := r.Data(nil) - d.SetId(arn) + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Image Builder Component sweep for %s: %s", region, err) + return nil + } - sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) - } + if err != nil { + return fmt.Errorf("error listing Image Builder Components (%s): %w", region, err) + } - return !lastPage - }) + for _, v := range page.ComponentVersionList { + r := resourceComponent() + d := r.Data(nil) + d.SetId(aws.ToString(v.Arn)) - if err != nil { - sweeperErr := fmt.Errorf("error listing Image Builder Component (%s) versions: %w", arn, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - continue - } + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + } - return !lastPage - }) + err = sweep.SweepOrchestrator(ctx, sweepResources) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping Image Builder Component sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Image Builder Components: %w", err)) - } - - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Image Builder Components: %w", err)) + return fmt.Errorf("error sweeping Image Builder Components (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepDistributionConfigurations(region string) error { @@ -135,47 +108,39 @@ func sweepDistributionConfigurations(region string) error { if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.ImageBuilderConn(ctx) - + conn := client.ImageBuilderClient(ctx) + input := &imagebuilder.ListDistributionConfigurationsInput{} sweepResources := make([]sweep.Sweepable, 0) - var sweeperErrs *multierror.Error - input := &imagebuilder.ListDistributionConfigurationsInput{} - err = conn.ListDistributionConfigurationsPagesWithContext(ctx, input, func(page *imagebuilder.ListDistributionConfigurationsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + pages := imagebuilder.NewListDistributionConfigurationsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - for _, distributionConfigurationSummary := range page.DistributionConfigurationSummaryList { - if distributionConfigurationSummary == nil { - continue - } + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Image Builder Distribution Configuration sweep for %s: %s", region, err) + return nil + } - arn := aws.StringValue(distributionConfigurationSummary.Arn) + if err != nil { + return fmt.Errorf("error listing Image Builder Distribution Configurations (%s): %w", region, err) + } - r := ResourceDistributionConfiguration() + for _, v := range page.DistributionConfigurationSummaryList { + r := resourceDistributionConfiguration() d := r.Data(nil) - d.SetId(arn) + d.SetId(aws.ToString(v.Arn)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + } - return !lastPage - }) + err = sweep.SweepOrchestrator(ctx, sweepResources) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping Image Builder Distribution Configuration sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Image Builder Distribution Configurations: %w", err)) + return fmt.Errorf("error sweeping Image Builder Distribution Configuration Summary (%s): %w", region, err) } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Image Builder Distribution Configurations: %w", err)) - } - - return sweeperErrs.ErrorOrNil() + return nil } func sweepImagePipelines(region string) error { @@ -184,47 +149,39 @@ func sweepImagePipelines(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.ImageBuilderConn(ctx) - + conn := client.ImageBuilderClient(ctx) + input := &imagebuilder.ListImagePipelinesInput{} sweepResources := make([]sweep.Sweepable, 0) - var sweeperErrs *multierror.Error - input := &imagebuilder.ListImagePipelinesInput{} - err = conn.ListImagePipelinesPagesWithContext(ctx, input, func(page *imagebuilder.ListImagePipelinesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + pages := imagebuilder.NewListImagePipelinesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - for _, imagePipeline := range page.ImagePipelineList { - if imagePipeline == nil { - continue - } + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Image Builder Image Pipeline sweep for %s: %s", region, err) + return nil + } - arn := aws.StringValue(imagePipeline.Arn) + if err != nil { + return fmt.Errorf("error listing Image Builder Image Pipelines (%s): %w", region, err) + } - r := ResourceImagePipeline() + for _, v := range page.ImagePipelineList { + r := resourceImagePipeline() d := r.Data(nil) - d.SetId(arn) + d.SetId(aws.ToString(v.Arn)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + } - return !lastPage - }) + err = sweep.SweepOrchestrator(ctx, sweepResources) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping Image Builder Image Pipeline sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Image Builder Image Pipelines: %w", err)) - } - - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Image Builder Image Pipelines: %w", err)) + return fmt.Errorf("error sweeping Image Builder Image Pipelines (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepImageRecipes(region string) error { @@ -233,49 +190,39 @@ func sweepImageRecipes(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.ImageBuilderConn(ctx) - + conn := client.ImageBuilderClient(ctx) + input := &imagebuilder.ListImageRecipesInput{} sweepResources := make([]sweep.Sweepable, 0) - var sweeperErrs *multierror.Error - input := &imagebuilder.ListImageRecipesInput{ - Owner: aws.String(imagebuilder.OwnershipSelf), - } - err = conn.ListImageRecipesPagesWithContext(ctx, input, func(page *imagebuilder.ListImageRecipesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + pages := imagebuilder.NewListImageRecipesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - for _, imageRecipeSummary := range page.ImageRecipeSummaryList { - if imageRecipeSummary == nil { - continue - } + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Image Builder Image Recipe sweep for %s: %s", region, err) + return nil + } - arn := aws.StringValue(imageRecipeSummary.Arn) + if err != nil { + return fmt.Errorf("error listing Image Builder Image Recipes (%s): %w", region, err) + } - r := ResourceImageRecipe() + for _, v := range page.ImageRecipeSummaryList { + r := resourceImageRecipe() d := r.Data(nil) - d.SetId(arn) + d.SetId(aws.ToString(v.Arn)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + } - return !lastPage - }) + err = sweep.SweepOrchestrator(ctx, sweepResources) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping Image Builder Image Recipe sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Image Builder Image Recipes: %w", err)) - } - - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Image Builder Image Recipes: %w", err)) + return fmt.Errorf("error sweeping Image Builder Image Recipes (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepContainerRecipes(region string) error { @@ -284,178 +231,156 @@ func sweepContainerRecipes(region string) error { if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.ImageBuilderConn(ctx) - + conn := client.ImageBuilderClient(ctx) + input := &imagebuilder.ListContainerRecipesInput{} sweepResources := make([]sweep.Sweepable, 0) - var sweeperErrs *multierror.Error - input := &imagebuilder.ListContainerRecipesInput{ - Owner: aws.String(imagebuilder.OwnershipSelf), - } - err = conn.ListContainerRecipesPagesWithContext(ctx, input, func(page *imagebuilder.ListContainerRecipesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + pages := imagebuilder.NewListContainerRecipesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - for _, containerRecipeSummary := range page.ContainerRecipeSummaryList { - if containerRecipeSummary == nil { - continue - } + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Image Builder Container Recipe sweep for %s: %s", region, err) + return nil + } - arn := aws.StringValue(containerRecipeSummary.Arn) + if err != nil { + return fmt.Errorf("error listing Image Builder Container Recipes (%s): %w", region, err) + } - r := ResourceContainerRecipe() + for _, v := range page.ContainerRecipeSummaryList { + r := resourceContainerRecipe() d := r.Data(nil) - d.SetId(arn) + d.SetId(aws.ToString(v.Arn)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + } - return !lastPage - }) + err = sweep.SweepOrchestrator(ctx, sweepResources) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping Image Builder Container Recipe sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Image Builder Container Recipes: %w", err)) - } - - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Image Builder Container Recipes: %w", err)) + return fmt.Errorf("error sweeping Image Builder Container Recipes (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } func sweepImages(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - - conn := client.ImageBuilderConn(ctx) + conn := client.ImageBuilderClient(ctx) + input := &imagebuilder.ListImagesInput{} sweepResources := make([]sweep.Sweepable, 0) - var errs *multierror.Error - input := &imagebuilder.ListImagesInput{ - Owner: aws.String(imagebuilder.OwnershipSelf), - } + pages := imagebuilder.NewListImagesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - err = conn.ListImagesPagesWithContext(ctx, input, func(page *imagebuilder.ListImagesOutput, lastPage bool) bool { - if page == nil { - return !lastPage + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Image Builder Image sweep for %s: %s", region, err) + return nil } - for _, imageVersion := range page.ImageVersionList { - if imageVersion == nil { - continue - } + if err != nil { + return fmt.Errorf("error listing Image Builder Images (%s): %w", region, err) + } - // Retrieve the Image's Build Version ARNs required as input - // to the ResourceImage()'s Delete operation - // Reference: /~https://github.com/hashicorp/terraform-provider-aws/issues/19851 - imageVersionArn := aws.StringValue(imageVersion.Arn) + for _, v := range page.ImageVersionList { + r := resourceImage() + d := r.Data(nil) + d.SetId(aws.ToString(v.Arn)) - input := &imagebuilder.ListImageBuildVersionsInput{ - ImageVersionArn: imageVersion.Arn, - } + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } + } - err := conn.ListImageBuildVersionsPagesWithContext(ctx, input, func(page *imagebuilder.ListImageBuildVersionsOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + err = sweep.SweepOrchestrator(ctx, sweepResources) - for _, imageSummary := range page.ImageSummaryList { - if imageSummary == nil { - continue - } + if err != nil { + return fmt.Errorf("error sweeping Image Builder Images (%s): %w", region, err) + } - imageBuildVersionArn := aws.StringValue(imageSummary.Arn) + return nil +} - r := ResourceImage() - d := r.Data(nil) - d.SetId(imageBuildVersionArn) +func sweepInfrastructureConfigurations(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.ImageBuilderClient(ctx) + input := &imagebuilder.ListInfrastructureConfigurationsInput{} + sweepResources := make([]sweep.Sweepable, 0) - sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) - } + pages := imagebuilder.NewListInfrastructureConfigurationsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - return !lastPage - }) + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Image Builder Infrastructure Configuration sweep for %s: %s", region, err) + return nil + } - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing Image Builder Image Build Versions for image (%s): %w", imageVersionArn, err)) - } + if err != nil { + return fmt.Errorf("error listing Image Builder Infrastructure Configurations (%s): %w", region, err) } - return !lastPage - }) + for _, v := range page.InfrastructureConfigurationSummaryList { + r := resourceInfrastructureConfiguration() + d := r.Data(nil) + d.SetId(aws.ToString(v.Arn)) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing Image Builder Images for %s: %w", region, err)) + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping Image Builder Images for %s: %w", region, err)) - } + err = sweep.SweepOrchestrator(ctx, sweepResources) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping Image Builder Image sweep for %s: %s", region, err) - return nil + if err != nil { + return fmt.Errorf("error sweeping Image Builder Infrastructure Configurations (%s): %w", region, err) } - return errs.ErrorOrNil() + return nil } -func sweepInfrastructureConfigurations(region string) error { +func sweepLifecyclePolicies(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) if err != nil { return fmt.Errorf("error getting client: %s", err) } - conn := client.ImageBuilderConn(ctx) - + conn := client.ImageBuilderClient(ctx) + input := &imagebuilder.ListLifecyclePoliciesInput{} sweepResources := make([]sweep.Sweepable, 0) - var sweeperErrs *multierror.Error - input := &imagebuilder.ListInfrastructureConfigurationsInput{} + pages := imagebuilder.NewListLifecyclePoliciesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) - err = conn.ListInfrastructureConfigurationsPagesWithContext(ctx, input, func(page *imagebuilder.ListInfrastructureConfigurationsOutput, lastPage bool) bool { - if page == nil { - return !lastPage + if awsv2.SkipSweepError(err) { + log.Printf("[WARN] Skipping Image Builder Lifecycle Policy sweep for %s: %s", region, err) + return nil } - for _, infrastructureConfigurationSummary := range page.InfrastructureConfigurationSummaryList { - if infrastructureConfigurationSummary == nil { - continue - } - - arn := aws.StringValue(infrastructureConfigurationSummary.Arn) - - r := ResourceInfrastructureConfiguration() - d := r.Data(nil) - d.SetId(arn) + if err != nil { + return fmt.Errorf("error listing Image Builder Lifecycle Policies (%s): %w", region, err) + } - sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + for _, v := range page.LifecyclePolicySummaryList { + sweepResources = append(sweepResources, framework.NewSweepResource(newLifecyclePolicyResource, client, framework.NewAttribute(names.AttrARN, v.Arn))) } + } - return !lastPage - }) + err = sweep.SweepOrchestrator(ctx, sweepResources) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping Image Builder Infrastructure Configuration sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Image Builder Infrastructure Configurations: %w", err)) - } - - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Image Builder Infrastructure Configurations: %w", err)) + return fmt.Errorf("error sweeping Image Builder Lifecycle Policies (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } diff --git a/internal/service/imagebuilder/tags_gen.go b/internal/service/imagebuilder/tags_gen.go index 9c11e5da020..553a9befa11 100644 --- a/internal/service/imagebuilder/tags_gen.go +++ b/internal/service/imagebuilder/tags_gen.go @@ -5,9 +5,8 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/aws/aws-sdk-go/service/imagebuilder/imagebuilderiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/logging" @@ -19,12 +18,12 @@ import ( // listTags lists imagebuilder service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func listTags(ctx context.Context, conn imagebuilderiface.ImagebuilderAPI, identifier string) (tftags.KeyValueTags, error) { +func listTags(ctx context.Context, conn *imagebuilder.Client, identifier string, optFns ...func(*imagebuilder.Options)) (tftags.KeyValueTags, error) { input := &imagebuilder.ListTagsForResourceInput{ ResourceArn: aws.String(identifier), } - output, err := conn.ListTagsForResourceWithContext(ctx, input) + output, err := conn.ListTagsForResource(ctx, input, optFns...) if err != nil { return tftags.New(ctx, nil), err @@ -36,7 +35,7 @@ func listTags(ctx context.Context, conn imagebuilderiface.ImagebuilderAPI, ident // ListTags lists imagebuilder service tags and set them in Context. // It is called from outside this package. func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier string) error { - tags, err := listTags(ctx, meta.(*conns.AWSClient).ImageBuilderConn(ctx), identifier) + tags, err := listTags(ctx, meta.(*conns.AWSClient).ImageBuilderClient(ctx), identifier) if err != nil { return err @@ -49,21 +48,21 @@ func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier stri return nil } -// map[string]*string handling +// map[string]string handling // Tags returns imagebuilder service tags. -func Tags(tags tftags.KeyValueTags) map[string]*string { - return aws.StringMap(tags.Map()) +func Tags(tags tftags.KeyValueTags) map[string]string { + return tags.Map() } // KeyValueTags creates tftags.KeyValueTags from imagebuilder service tags. -func KeyValueTags(ctx context.Context, tags map[string]*string) tftags.KeyValueTags { +func KeyValueTags(ctx context.Context, tags map[string]string) tftags.KeyValueTags { return tftags.New(ctx, tags) } // getTagsIn returns imagebuilder service tags from Context. // nil is returned if there are no input tags. -func getTagsIn(ctx context.Context) map[string]*string { +func getTagsIn(ctx context.Context) map[string]string { if inContext, ok := tftags.FromContext(ctx); ok { if tags := Tags(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { return tags @@ -74,7 +73,7 @@ func getTagsIn(ctx context.Context) map[string]*string { } // setTagsOut sets imagebuilder service tags in Context. -func setTagsOut(ctx context.Context, tags map[string]*string) { +func setTagsOut(ctx context.Context, tags map[string]string) { if inContext, ok := tftags.FromContext(ctx); ok { inContext.TagsOut = option.Some(KeyValueTags(ctx, tags)) } @@ -83,7 +82,7 @@ func setTagsOut(ctx context.Context, tags map[string]*string) { // updateTags updates imagebuilder service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. -func updateTags(ctx context.Context, conn imagebuilderiface.ImagebuilderAPI, identifier string, oldTagsMap, newTagsMap any) error { +func updateTags(ctx context.Context, conn *imagebuilder.Client, identifier string, oldTagsMap, newTagsMap any, optFns ...func(*imagebuilder.Options)) error { oldTags := tftags.New(ctx, oldTagsMap) newTags := tftags.New(ctx, newTagsMap) @@ -94,10 +93,10 @@ func updateTags(ctx context.Context, conn imagebuilderiface.ImagebuilderAPI, ide if len(removedTags) > 0 { input := &imagebuilder.UntagResourceInput{ ResourceArn: aws.String(identifier), - TagKeys: aws.StringSlice(removedTags.Keys()), + TagKeys: removedTags.Keys(), } - _, err := conn.UntagResourceWithContext(ctx, input) + _, err := conn.UntagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("untagging resource (%s): %w", identifier, err) @@ -112,7 +111,7 @@ func updateTags(ctx context.Context, conn imagebuilderiface.ImagebuilderAPI, ide Tags: Tags(updatedTags), } - _, err := conn.TagResourceWithContext(ctx, input) + _, err := conn.TagResource(ctx, input, optFns...) if err != nil { return fmt.Errorf("tagging resource (%s): %w", identifier, err) @@ -125,5 +124,5 @@ func updateTags(ctx context.Context, conn imagebuilderiface.ImagebuilderAPI, ide // UpdateTags updates imagebuilder 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 updateTags(ctx, meta.(*conns.AWSClient).ImageBuilderConn(ctx), identifier, oldTags, newTags) + return updateTags(ctx, meta.(*conns.AWSClient).ImageBuilderClient(ctx), identifier, oldTags, newTags) } diff --git a/internal/service/imagebuilder/wait.go b/internal/service/imagebuilder/wait.go deleted file mode 100644 index ac4b4d2aa76..00000000000 --- a/internal/service/imagebuilder/wait.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package imagebuilder - -import ( - "context" - "time" - - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" -) - -// waitImageStatusAvailable waits for an Image to return Available -func waitImageStatusAvailable(ctx context.Context, conn *imagebuilder.Imagebuilder, imageBuildVersionArn string, timeout time.Duration) (*imagebuilder.Image, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{ - imagebuilder.ImageStatusBuilding, - imagebuilder.ImageStatusCreating, - imagebuilder.ImageStatusDistributing, - imagebuilder.ImageStatusIntegrating, - imagebuilder.ImageStatusPending, - imagebuilder.ImageStatusTesting, - }, - Target: []string{imagebuilder.ImageStatusAvailable}, - Refresh: statusImage(ctx, conn, imageBuildVersionArn), - Timeout: timeout, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - - if v, ok := outputRaw.(*imagebuilder.Image); ok { - return v, err - } - - return nil, err -} diff --git a/internal/service/imagebuilder/workflow.go b/internal/service/imagebuilder/workflow.go index bd8af56ab1a..e89d9e9fec7 100644 --- a/internal/service/imagebuilder/workflow.go +++ b/internal/service/imagebuilder/workflow.go @@ -8,28 +8,33 @@ import ( "log" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder" + awstypes "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_imagebuilder_workflow", name="Workflow") // @Tags(identifierAttribute="id") -func ResourceWorkflow() *schema.Resource { +func resourceWorkflow() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceWorkflowCreate, ReadWithoutTimeout: resourceWorkflowRead, UpdateWithoutTimeout: resourceWorkflowUpdate, DeleteWithoutTimeout: resourceWorkflowDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -82,10 +87,10 @@ func ResourceWorkflow() *schema.Resource { names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), names.AttrType: { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(imagebuilder.WorkflowType_Values(), false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[awstypes.WorkflowType](), }, names.AttrURI: { Type: schema.TypeString, @@ -107,13 +112,14 @@ func ResourceWorkflow() *schema.Resource { func resourceWorkflowCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) + name := d.Get(names.AttrName).(string) input := &imagebuilder.CreateWorkflowInput{ ClientToken: aws.String(id.UniqueId()), - Name: aws.String(d.Get(names.AttrName).(string)), + Name: aws.String(name), SemanticVersion: aws.String(d.Get(names.AttrVersion).(string)), - Type: aws.String(d.Get(names.AttrType).(string)), + Type: awstypes.WorkflowType(d.Get(names.AttrType).(string)), Tags: getTagsIn(ctx), } @@ -137,43 +143,33 @@ func resourceWorkflowCreate(ctx context.Context, d *schema.ResourceData, meta in input.Uri = aws.String(v.(string)) } - output, err := conn.CreateWorkflowWithContext(ctx, input) + output, err := conn.CreateWorkflow(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "creating Image Builder Workflow: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Image Builder Workflow (%s): %s", name, err) } - if output == nil { - return sdkdiag.AppendErrorf(diags, "creating Image Builder Workflow: empty response") - } - - d.SetId(aws.StringValue(output.WorkflowBuildVersionArn)) + d.SetId(aws.ToString(output.WorkflowBuildVersionArn)) return append(diags, resourceWorkflowRead(ctx, d, meta)...) } func resourceWorkflowRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.GetWorkflowInput{ - WorkflowBuildVersionArn: aws.String(d.Id()), - } + workflow, err := findWorkflowByARN(ctx, conn, d.Id()) - output, err := conn.GetWorkflowWithContext(ctx, input) - - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Image Builder Workflow (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - if output == nil || output.Workflow == nil { - return sdkdiag.AppendErrorf(diags, "getting Image Builder Workflow (%s): empty response", d.Id()) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Image Builder Workflow (%s): %s", d.Id(), err) } - workflow := output.Workflow - d.Set(names.AttrARN, workflow.Arn) d.Set("change_description", workflow.ChangeDescription) d.Set("data", workflow.Data) @@ -182,12 +178,11 @@ func resourceWorkflowRead(ctx context.Context, d *schema.ResourceData, meta inte d.Set(names.AttrName, workflow.Name) d.Set(names.AttrKMSKeyID, workflow.KmsKeyId) d.Set(names.AttrOwner, workflow.Owner) - - setTagsOut(ctx, workflow.Tags) - d.Set(names.AttrType, workflow.Type) d.Set(names.AttrVersion, workflow.Version) + setTagsOut(ctx, workflow.Tags) + return diags } @@ -201,15 +196,14 @@ func resourceWorkflowUpdate(ctx context.Context, d *schema.ResourceData, meta in func resourceWorkflowDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ImageBuilderConn(ctx) + conn := meta.(*conns.AWSClient).ImageBuilderClient(ctx) - input := &imagebuilder.DeleteWorkflowInput{ + log.Printf("[DEBUG] Deleting Image Builder Workflow: %s", d.Id()) + _, err := conn.DeleteWorkflow(ctx, &imagebuilder.DeleteWorkflowInput{ WorkflowBuildVersionArn: aws.String(d.Id()), - } - - _, err := conn.DeleteWorkflowWithContext(ctx, input) + }) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { return diags } @@ -219,3 +213,28 @@ func resourceWorkflowDelete(ctx context.Context, d *schema.ResourceData, meta in return diags } + +func findWorkflowByARN(ctx context.Context, conn *imagebuilder.Client, arn string) (*awstypes.Workflow, error) { + input := &imagebuilder.GetWorkflowInput{ + WorkflowBuildVersionArn: aws.String(arn), + } + + output, err := conn.GetWorkflow(ctx, input) + + if tfawserr.ErrCodeEquals(err, errCodeResourceNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Workflow == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.Workflow, nil +} diff --git a/internal/service/imagebuilder/workflow_test.go b/internal/service/imagebuilder/workflow_test.go index 99c4ca25ffc..25f634925d2 100644 --- a/internal/service/imagebuilder/workflow_test.go +++ b/internal/service/imagebuilder/workflow_test.go @@ -9,15 +9,14 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/imagebuilder" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/service/imagebuilder/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfimagebuilder "github.com/hashicorp/terraform-provider-aws/internal/service/imagebuilder" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -45,7 +44,7 @@ func TestAccImageBuilderWorkflow_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), acctest.CheckResourceAttrAccountID(resourceName, names.AttrOwner), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), - resource.TestCheckResourceAttr(resourceName, names.AttrType, imagebuilder.WorkflowTypeTest), + resource.TestCheckResourceAttr(resourceName, names.AttrType, string(types.WorkflowTypeTest)), resource.TestCheckResourceAttr(resourceName, names.AttrVersion, "1.0.0"), ), }, @@ -239,56 +238,42 @@ func TestAccImageBuilderWorkflow_uri(t *testing.T) { func testAccCheckWorkflowDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) for _, rs := range s.RootModule().Resources { if rs.Type != "aws_imagebuilder_workflow" { continue } - input := &imagebuilder.GetWorkflowInput{ - WorkflowBuildVersionArn: aws.String(rs.Primary.ID), - } - - output, err := conn.GetWorkflowWithContext(ctx, input) + _, err := tfimagebuilder.FindWorkflowByARN(ctx, conn, rs.Primary.ID) - if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + if tfresource.NotFound(err) { continue } if err != nil { - return fmt.Errorf("error getting Image Builder Workflow (%s): %w", rs.Primary.ID, err) + return err } - if output != nil { - return fmt.Errorf("Image Builder Workflow (%s) still exists", rs.Primary.ID) - } + return fmt.Errorf("Image Builder Workflow %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckWorkflowExists(ctx context.Context, resourceName string) resource.TestCheckFunc { +func testAccCheckWorkflowExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("resource not found: %s", resourceName) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) - - input := &imagebuilder.GetWorkflowInput{ - WorkflowBuildVersionArn: aws.String(rs.Primary.ID), + return fmt.Errorf("Not found: %s", n) } - _, err := conn.GetWorkflowWithContext(ctx, input) + conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderClient(ctx) - if err != nil { - return fmt.Errorf("error getting Image Builder Workflow (%s): %w", rs.Primary.ID, err) - } + _, err := tfimagebuilder.FindWorkflowByARN(ctx, conn, rs.Primary.ID) - return nil + return err } } diff --git a/internal/tfresource/retry.go b/internal/tfresource/retry.go index abaec1d39d3..5e7d6e216e2 100644 --- a/internal/tfresource/retry.go +++ b/internal/tfresource/retry.go @@ -11,8 +11,7 @@ import ( "sync" "time" - tfawserr_sdkv1 "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" - tfawserr_sdkv2 "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" + "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/errs" ) @@ -96,7 +95,7 @@ func RetryGWhen[T any](ctx context.Context, timeout time.Duration, f func() (T, // RetryWhenAWSErrCodeEquals retries the specified function when it returns one of the specified AWS error codes. func RetryWhenAWSErrCodeEquals(ctx context.Context, timeout time.Duration, f func() (interface{}, error), codes ...string) (interface{}, error) { // nosemgrep:ci.aws-in-func-name return RetryWhen(ctx, timeout, f, func(err error) (bool, error) { - if tfawserr_sdkv1.ErrCodeEquals(err, codes...) || tfawserr_sdkv2.ErrCodeEquals(err, codes...) { + if tfawserr.ErrCodeEquals(err, codes...) { return true, err } @@ -107,7 +106,7 @@ func RetryWhenAWSErrCodeEquals(ctx context.Context, timeout time.Duration, f fun // RetryGWhenAWSErrCodeEquals retries the specified function when it returns one of the specified AWS error codes. func RetryGWhenAWSErrCodeEquals[T any](ctx context.Context, timeout time.Duration, f func() (T, error), codes ...string) (T, error) { // nosemgrep:ci.aws-in-func-name return RetryGWhen(ctx, timeout, f, func(err error) (bool, error) { - if tfawserr_sdkv1.ErrCodeEquals(err, codes...) || tfawserr_sdkv2.ErrCodeEquals(err, codes...) { + if tfawserr.ErrCodeEquals(err, codes...) { return true, err } @@ -118,7 +117,7 @@ func RetryGWhenAWSErrCodeEquals[T any](ctx context.Context, timeout time.Duratio // RetryWhenAWSErrCodeContains retries the specified function when it returns an AWS error containing the specified code. func RetryWhenAWSErrCodeContains(ctx context.Context, timeout time.Duration, f func() (interface{}, error), code string) (interface{}, error) { // nosemgrep:ci.aws-in-func-name return RetryWhen(ctx, timeout, f, func(err error) (bool, error) { - if tfawserr_sdkv1.ErrCodeContains(err, code) || tfawserr_sdkv2.ErrCodeContains(err, code) { + if tfawserr.ErrCodeContains(err, code) { return true, err } @@ -129,7 +128,7 @@ func RetryWhenAWSErrCodeContains(ctx context.Context, timeout time.Duration, f f // RetryWhenAWSErrMessageContains retries the specified function when it returns an AWS error containing the specified message. func RetryWhenAWSErrMessageContains(ctx context.Context, timeout time.Duration, f func() (interface{}, error), code, message string) (interface{}, error) { // nosemgrep:ci.aws-in-func-name return RetryWhen(ctx, timeout, f, func(err error) (bool, error) { - if tfawserr_sdkv1.ErrMessageContains(err, code, message) || tfawserr_sdkv2.ErrMessageContains(err, code, message) { + if tfawserr.ErrMessageContains(err, code, message) { return true, err } diff --git a/internal/tfresource/retry_test.go b/internal/tfresource/retry_test.go index 7c095fdd689..33031199d8e 100644 --- a/internal/tfresource/retry_test.go +++ b/internal/tfresource/retry_test.go @@ -10,7 +10,6 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -21,8 +20,6 @@ func TestRetryWhenAWSErrCodeEquals(t *testing.T) { // nosemgrep:ci.aws-in-func-n ctx := acctest.Context(t) t.Parallel() - var retryCount int32 - testCases := []struct { Name string F func() (interface{}, error) @@ -41,36 +38,10 @@ func TestRetryWhenAWSErrCodeEquals(t *testing.T) { // nosemgrep:ci.aws-in-func-n }, ExpectError: true, }, - { - Name: "non-retryable AWS error", - F: func() (interface{}, error) { - return nil, awserr.New("Testing", "Testing", nil) - }, - ExpectError: true, - }, - { - Name: "retryable AWS error timeout", - F: func() (interface{}, error) { - return nil, awserr.New("TestCode1", "TestMessage", nil) - }, - ExpectError: true, - }, - { - Name: "retryable AWS error success", - F: func() (interface{}, error) { - if atomic.CompareAndSwapInt32(&retryCount, 0, 1) { - return nil, awserr.New("TestCode2", "TestMessage", nil) - } - - return nil, nil - }, - }, } for _, testCase := range testCases { //nolint:paralleltest t.Run(testCase.Name, func(t *testing.T) { - retryCount = 0 - _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, 5*time.Second, testCase.F, "TestCode1", "TestCode2") if testCase.ExpectError && err == nil { @@ -87,8 +58,6 @@ func TestRetryWhenAWSErrMessageContains(t *testing.T) { // nosemgrep:ci.aws-in-f ctx := acctest.Context(t) t.Parallel() - var retryCount int32 - testCases := []struct { Name string F func() (interface{}, error) @@ -107,36 +76,10 @@ func TestRetryWhenAWSErrMessageContains(t *testing.T) { // nosemgrep:ci.aws-in-f }, ExpectError: true, }, - { - Name: "non-retryable AWS error", - F: func() (interface{}, error) { - return nil, awserr.New("TestCode1", "Testing", nil) - }, - ExpectError: true, - }, - { - Name: "retryable AWS error timeout", - F: func() (interface{}, error) { - return nil, awserr.New("TestCode1", "TestMessage1", nil) - }, - ExpectError: true, - }, - { - Name: "retryable AWS error success", - F: func() (interface{}, error) { - if atomic.CompareAndSwapInt32(&retryCount, 0, 1) { - return nil, awserr.New("TestCode1", "TestMessage1", nil) - } - - return nil, nil - }, - }, } for _, testCase := range testCases { //nolint:paralleltest t.Run(testCase.Name, func(t *testing.T) { - retryCount = 0 - _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, 5*time.Second, testCase.F, "TestCode1", "TestMessage1") if testCase.ExpectError && err == nil { @@ -188,13 +131,6 @@ func TestRetryWhenNewResourceNotFound(t *testing.T) { //nolint:tparallel NewResource: true, ExpectError: true, }, - { - Name: "non-retryable AWS error", - F: func() (interface{}, error) { - return nil, awserr.New("Testing", "Testing", nil) - }, - ExpectError: true, - }, { Name: "retryable NotFoundError not new resource", F: func() (interface{}, error) { @@ -262,13 +198,6 @@ func TestRetryWhenNotFound(t *testing.T) { //nolint:tparallel }, ExpectError: true, }, - { - Name: "non-retryable AWS error", - F: func() (interface{}, error) { - return nil, awserr.New("Testing", "Testing", nil) - }, - ExpectError: true, - }, { Name: "retryable NotFoundError timeout", F: func() (interface{}, error) { @@ -328,13 +257,6 @@ func TestRetryUntilNotFound(t *testing.T) { //nolint:tparallel }, ExpectError: true, }, - { - Name: "AWS error", - F: func() (interface{}, error) { - return nil, awserr.New("Testing", "Testing", nil) - }, - ExpectError: true, - }, { Name: "NotFoundError", F: func() (interface{}, error) { diff --git a/names/data/names_data.hcl b/names/data/names_data.hcl index e4ba54d340e..985bf531d03 100644 --- a/names/data/names_data.hcl +++ b/names/data/names_data.hcl @@ -2945,8 +2945,7 @@ service "ebs" { service "imagebuilder" { sdk { - id = "imagebuilder" - client_version = 1 + id = "imagebuilder" } names { diff --git a/names/names.go b/names/names.go index f3ff422b2f4..4f96482fb16 100644 --- a/names/names.go +++ b/names/names.go @@ -68,6 +68,7 @@ const ( ConnectEndpointID = "connect" DataExchangeEndpointID = "dataexchange" DataPipelineEndpointID = "datapipeline" + DataZoneEndpointID = "datazone" DetectiveEndpointID = "api.detective" DeviceFarmEndpointID = "devicefarm" DevOpsGuruEndpointID = "devops-guru" @@ -92,7 +93,9 @@ const ( IVSEndpointID = "ivs" IVSChatEndpointID = "ivschat" IdentityStoreEndpointID = "identitystore" + ImageBuilderEndpointID = "imagebuilder" Inspector2EndpointID = "inspector2" + InternetMonitorEndpointID = "internetmonitor" KMSEndpointID = "kms" KafkaConnectEndpointID = "kafkaconnect" KendraEndpointID = "kendra" @@ -140,7 +143,6 @@ const ( VerifiedPermissionsEndpointID = "verifiedpermissions" WAFEndpointID = "waf" WAFRegionalEndpointID = "waf-regional" - DataZoneEndpointID = "datazone" ) // These should move to aws-sdk-go-base. diff --git a/website/docs/r/imagebuilder_lifecycle_policy.html.markdown b/website/docs/r/imagebuilder_lifecycle_policy.html.markdown new file mode 100644 index 00000000000..9503d859344 --- /dev/null +++ b/website/docs/r/imagebuilder_lifecycle_policy.html.markdown @@ -0,0 +1,184 @@ +--- +subcategory: "EC2 Image Builder" +layout: "aws" +page_title: "AWS: aws_imagebuilder_lifecycle_policy" +description: |- + Manages an Image Builder Lifecycle Policy +--- + +# Resource: aws_imagebuilder_lifecycle_policy + +Manages an Image Builder Lifecycle Policy. + +## Example Usage + +```terraform +data "aws_region" "current" {} + +data "aws_partition" "current" {} + +resource "aws_iam_role" "example" { + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "imagebuilder.${data.aws_partition.current.dns_suffix}" + } + }] + }) + name = "example" +} + +resource "aws_iam_role_policy_attachment" "example" { + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/EC2ImageBuilderLifecycleExecutionPolicy" + role = aws_iam_role.example.name +} + +resource "aws_imagebuilder_lifecycle_policy" "example" { + name = "name" + description = "Example description" + execution_role = aws_iam_role.example.arn + resource_type = "AMI_IMAGE" + policy_detail { + action { + type = "DELETE" + } + filter { + type = "AGE" + value = 6 + retain_at_least = 10 + unit = "YEARS" + } + } + resource_selection { + tag_map = { + "key1" = "value1" + "key2" = "value2" + } + } + + depends_on = [aws_iam_role_policy_attachment.example] +} +``` + +## Argument Reference + +The following arguments are required: + +* `name` - (Required) The name of the lifecycle policy to create. +* `resource_type` - (Required) The type of Image Builder resource that the lifecycle policy applies to. Valid values: `AMI_IMAGE` or `CONTAINER_IMAGE`. +* `execution_role` - (Required) The Amazon Resource Name (ARN) for the IAM role you create that grants Image Builder access to run lifecycle actions. More information about this role can be found [`here`](https://docs.aws.amazon.com/imagebuilder/latest/userguide/image-lifecycle-prerequisites.html#image-lifecycle-prereq-role). +* `policy_detail` - (Required) Configuration block with policy details. Detailed below. +* `resource_selection` - (Required) Selection criteria for the resources that the lifecycle policy applies to. Detailed below. + +The following arguments are optional: + +* `description` - (Optional) description for the lifecycle policy. +* `tags` - (Optional) Key-value map of resource tags for the Image Builder Lifecycle Policy. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +### policy_detail + +The following arguments are required: + +* `action` - (Required) Configuration details for the policy action. +* `filter` - (Required) Specifies the resources that the lifecycle policy applies to. + +The following arguments are optional: + +* `exclusion_rules` - (Optional) Additional rules to specify resources that should be exempt from policy actions. + +### action + +The following arguments are required: + +* `type` - (Required) Specifies the lifecycle action to take. Valid values: `DELETE`, `DEPRECATE` or `DISABLE`. + +The following arguments are optional: + +* `include_resources` - (Optional) Specifies the resources that the lifecycle policy applies to. Detailed below. + +### include_resources + +The following arguments are optional: + +* `amis` - (Optional) Specifies whether the lifecycle action should apply to distributed AMIs. +* `containers` - (Optional) Specifies whether the lifecycle action should apply to distributed containers. +* `snapshots` - (Optional) Specifies whether the lifecycle action should apply to snapshots associated with distributed AMIs. + +### filter + +The following arguments are required: + +* `type` - (Required) Filter resources based on either age or count. Valid values: `AGE` or `COUNT`. +* `value` - (Required) The number of units for the time period or for the count. For example, a value of 6 might refer to six months or six AMIs. + +The following arguments are optional: + +* `retain_at_least` - (Optional) For age-based filters, this is the number of resources to keep on hand after the lifecycle DELETE action is applied. Impacted resources are only deleted if you have more than this number of resources. If you have fewer resources than this number, the impacted resource is not deleted. +* `unit` - (Optional) Defines the unit of time that the lifecycle policy uses to determine impacted resources. This is required for age-based rules. Valid values: `DAYS`, `WEEKS`, `MONTHS` or `YEARS`. + +### exclusion_rules + +The following arguments are optional: + +* `amis` - (Optional) Lists configuration values that apply to AMIs that Image Builder should exclude from the lifecycle action. Detailed below. +* `tag_map` - (Optional) Contains a list of tags that Image Builder uses to skip lifecycle actions for Image Builder image resources that have them. + +### amis + +The following arguments are optional: + +* `is_public` - (Optional) Configures whether public AMIs are excluded from the lifecycle action. +* `last_launched` - (Optional) Specifies configuration details for Image Builder to exclude the most recent resources from lifecycle actions. Detailed below. +* `regions` - (Optional) Configures AWS Regions that are excluded from the lifecycle action. +* `shared_accounts` - Specifies AWS accounts whose resources are excluded from the lifecycle action. +* `tag_map` - (Optional) Lists tags that should be excluded from lifecycle actions for the AMIs that have them. + +### last_launched + +The following arguments are required: + +* `unit` - (Required) Defines the unit of time that the lifecycle policy uses to calculate elapsed time since the last instance launched from the AMI. For example: days, weeks, months, or years. Valid values: `DAYS`, `WEEKS`, `MONTHS` or `YEARS`. +* `value` - (Required) The integer number of units for the time period. For example 6 (months). + +### resource_selection + +The following arguments are optional: + +* `recipe` - (Optional) A list of recipe that are used as selection criteria for the output images that the lifecycle policy applies to. Detailed below. +* `tag_map` - (Optional) A list of tags that are used as selection criteria for the Image Builder image resources that the lifecycle policy applies to. + +### recipe + +The following arguments are required: + +* `name` - (Required) The name of an Image Builder recipe that the lifecycle policy uses for resource selection. +* `semantic_version` - (Required) The version of the Image Builder recipe specified by the name field. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `id` - Amazon Resource Name (ARN) of the lifecycle policy. +* `arn` - Amazon Resource Name (ARN) of the lifecycle policy. +* `status` - The status of the lifecycle policy. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import `aws_imagebuilder_lifecycle_policy` using the Amazon Resource Name (ARN). For example: + +```terraform +import { + to = aws_imagebuilder_lifecycle_policy.example + id = "arn:aws:imagebuilder:us-east-1:123456789012:lifecycle-policy/example" +} +``` + +Using `terraform import`, import `aws_imagebuilder_lifecycle_policy` using the Amazon Resource Name (ARN). For example: + +```console +% terraform import aws_imagebuilder_lifecycle_policy.example arn:aws:imagebuilder:us-east-1:123456789012:lifecycle-policy/example +```