diff --git a/aws/data_source_aws_imagebuilder_component.go b/aws/data_source_aws_imagebuilder_component.go new file mode 100644 index 00000000000..20fe6efb607 --- /dev/null +++ b/aws/data_source_aws_imagebuilder_component.go @@ -0,0 +1,120 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func dataSourceAwsImageBuilderComponent() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsImageBuilderComponentRead, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + "change_description": { + Type: schema.TypeString, + Computed: true, + }, + "data": { + Type: schema.TypeString, + Computed: true, + }, + "date_created": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "encrypted": { + Type: schema.TypeBool, + Computed: true, + }, + "kms_key_id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "owner": { + Type: schema.TypeString, + Computed: true, + }, + "platform": { + Type: schema.TypeString, + Computed: true, + }, + "supported_os_versions": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "tags": tagsSchemaComputed(), + "type": { + Type: schema.TypeString, + Computed: true, + }, + "version": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsImageBuilderComponentRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).imagebuilderconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + input := &imagebuilder.GetComponentInput{} + + if v, ok := d.GetOk("arn"); ok { + input.ComponentBuildVersionArn = aws.String(v.(string)) + } + + output, err := conn.GetComponent(input) + + if err != nil { + return fmt.Errorf("error getting Image Builder Component: %w", err) + } + + if output == nil || output.Component == nil { + return fmt.Errorf("error getting Image Builder Component: empty result") + } + + component := output.Component + + d.SetId(aws.StringValue(component.Arn)) + + d.Set("arn", component.Arn) + d.Set("change_description", component.ChangeDescription) + d.Set("data", component.Data) + d.Set("date_created", component.DateCreated) + d.Set("description", component.Description) + d.Set("encrypted", component.Encrypted) + d.Set("kms_key_id", component.KmsKeyId) + d.Set("name", component.Name) + d.Set("owner", component.Owner) + d.Set("platform", component.Platform) + d.Set("supported_os_versions", aws.StringValueSlice(component.SupportedOsVersions)) + + if err := d.Set("tags", keyvaluetags.ImagebuilderKeyValueTags(component.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + d.Set("type", component.Type) + d.Set("version", component.Version) + + return nil +} diff --git a/aws/data_source_aws_imagebuilder_component_test.go b/aws/data_source_aws_imagebuilder_component_test.go new file mode 100644 index 00000000000..feb31cfcc83 --- /dev/null +++ b/aws/data_source_aws_imagebuilder_component_test.go @@ -0,0 +1,70 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccAwsImageBuilderComponentDataSource_Arn(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + dataSourceName := "data.aws_imagebuilder_component.test" + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentDataSourceConfigBuildVersionArn(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(dataSourceName, "change_description", resourceName, "change_description"), + resource.TestCheckResourceAttrPair(dataSourceName, "data", resourceName, "data"), + resource.TestCheckResourceAttrPair(dataSourceName, "date_created", resourceName, "date_created"), + resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(dataSourceName, "encrypted", resourceName, "encrypted"), + resource.TestCheckResourceAttrPair(dataSourceName, "kms_key_id", resourceName, "kms_key_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(dataSourceName, "owner", resourceName, "owner"), + resource.TestCheckResourceAttrPair(dataSourceName, "platform", resourceName, "platform"), + resource.TestCheckResourceAttrPair(dataSourceName, "supported_os_versions.#", resourceName, "supported_os_versions.#"), + resource.TestCheckResourceAttrPair(dataSourceName, "tags.%", resourceName, "tags.%"), + resource.TestCheckResourceAttrPair(dataSourceName, "type", resourceName, "type"), + resource.TestCheckResourceAttrPair(dataSourceName, "version", resourceName, "version"), + ), + }, + }, + }) +} + +func testAccAwsImageBuilderComponentDataSourceConfigBuildVersionArn(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" +} + +data "aws_imagebuilder_component" "test" { + arn = aws_imagebuilder_component.test.arn +} +`, rName) +} diff --git a/aws/provider.go b/aws/provider.go index 976a61f49a0..4aea86da05b 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -266,6 +266,7 @@ func Provider() *schema.Provider { "aws_iam_role": dataSourceAwsIAMRole(), "aws_iam_server_certificate": dataSourceAwsIAMServerCertificate(), "aws_iam_user": dataSourceAwsIAMUser(), + "aws_imagebuilder_component": dataSourceAwsImageBuilderComponent(), "aws_internet_gateway": dataSourceAwsInternetGateway(), "aws_iot_endpoint": dataSourceAwsIotEndpoint(), "aws_inspector_rules_packages": dataSourceAwsInspectorRulesPackages(), @@ -698,6 +699,7 @@ func Provider() *schema.Provider { "aws_iam_user_ssh_key": resourceAwsIamUserSshKey(), "aws_iam_user": resourceAwsIamUser(), "aws_iam_user_login_profile": resourceAwsIamUserLoginProfile(), + "aws_imagebuilder_component": resourceAwsImageBuilderComponent(), "aws_inspector_assessment_target": resourceAWSInspectorAssessmentTarget(), "aws_inspector_assessment_template": resourceAWSInspectorAssessmentTemplate(), "aws_inspector_resource_group": resourceAWSInspectorResourceGroup(), diff --git a/aws/resource_aws_imagebuilder_component.go b/aws/resource_aws_imagebuilder_component.go new file mode 100644 index 00000000000..59fa135e242 --- /dev/null +++ b/aws/resource_aws_imagebuilder_component.go @@ -0,0 +1,255 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func resourceAwsImageBuilderComponent() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsImageBuilderComponentCreate, + Read: resourceAwsImageBuilderComponentRead, + Update: resourceAwsImageBuilderComponentUpdate, + Delete: resourceAwsImageBuilderComponentDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "change_description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "data": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ExactlyOneOf: []string{"data", "uri"}, + ValidateFunc: validation.StringLenBetween(1, 16000), + }, + "date_created": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, + "encrypted": { + Type: schema.TypeBool, + Computed: true, + }, + "kms_key_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 126), + }, + "owner": { + Type: schema.TypeString, + Computed: true, + }, + "platform": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(imagebuilder.Platform_Values(), false), + }, + "supported_os_versions": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + MinItems: 1, + MaxItems: 25, + }, + "tags": tagsSchema(), + "type": { + Type: schema.TypeString, + Computed: true, + }, + "uri": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ExactlyOneOf: []string{"data", "uri"}, + }, + "version": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + }, + } +} + +func resourceAwsImageBuilderComponentCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).imagebuilderconn + + input := &imagebuilder.CreateComponentInput{ + ClientToken: aws.String(resource.UniqueId()), + } + + if v, ok := d.GetOk("change_description"); ok { + input.ChangeDescription = aws.String(v.(string)) + } + + if v, ok := d.GetOk("data"); ok { + input.Data = aws.String(v.(string)) + } + + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } + + if v, ok := d.GetOk("kms_key_id"); ok { + input.KmsKeyId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("name"); ok { + input.Name = aws.String(v.(string)) + } + + if v, ok := d.GetOk("platform"); ok { + input.Platform = aws.String(v.(string)) + } + + if v, ok := d.GetOk("supported_os_versions"); ok && v.(*schema.Set).Len() > 0 { + input.SupportedOsVersions = expandStringSet(v.(*schema.Set)) + } + + if v, ok := d.GetOk("tags"); ok && len(v.(map[string]interface{})) > 0 { + input.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().ImagebuilderTags() + } + + if v, ok := d.GetOk("uri"); ok { + input.Uri = aws.String(v.(string)) + } + + if v, ok := d.GetOk("version"); ok { + input.SemanticVersion = aws.String(v.(string)) + } + + output, err := conn.CreateComponent(input) + + if err != nil { + return fmt.Errorf("error creating Image Builder Component: %w", err) + } + + if output == nil { + return fmt.Errorf("error creating Image Builder Component: empty result") + } + + d.SetId(aws.StringValue(output.ComponentBuildVersionArn)) + + return resourceAwsImageBuilderComponentRead(d, meta) +} + +func resourceAwsImageBuilderComponentRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).imagebuilderconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + input := &imagebuilder.GetComponentInput{ + ComponentBuildVersionArn: aws.String(d.Id()), + } + + output, err := conn.GetComponent(input) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Image Builder Component (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting Image Builder Component (%s): %w", d.Id(), err) + } + + if output == nil || output.Component == nil { + return fmt.Errorf("error getting Image Builder Component (%s): empty result", d.Id()) + } + + component := output.Component + + d.Set("arn", component.Arn) + d.Set("change_description", component.ChangeDescription) + d.Set("data", component.Data) + d.Set("date_created", component.DateCreated) + d.Set("description", component.Description) + d.Set("encrypted", component.Encrypted) + d.Set("kms_key_id", component.KmsKeyId) + d.Set("name", component.Name) + d.Set("owner", component.Owner) + d.Set("platform", component.Platform) + d.Set("supported_os_versions", aws.StringValueSlice(component.SupportedOsVersions)) + + if err := d.Set("tags", keyvaluetags.ImagebuilderKeyValueTags(component.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + d.Set("type", component.Type) + d.Set("version", component.Version) + + return nil +} + +func resourceAwsImageBuilderComponentUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).imagebuilderconn + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + if err := keyvaluetags.ImagebuilderUpdateTags(conn, d.Id(), o, n); err != nil { + return fmt.Errorf("error updating tags for Image Builder Component (%s): %w", d.Id(), err) + } + } + + return resourceAwsImageBuilderComponentRead(d, meta) +} + +func resourceAwsImageBuilderComponentDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).imagebuilderconn + + input := &imagebuilder.DeleteComponentInput{ + ComponentBuildVersionArn: aws.String(d.Id()), + } + + _, err := conn.DeleteComponent(input) + + if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting Image Builder Component (%s): %w", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_imagebuilder_component_test.go b/aws/resource_aws_imagebuilder_component_test.go new file mode 100644 index 00000000000..0bee0113e67 --- /dev/null +++ b/aws/resource_aws_imagebuilder_component_test.go @@ -0,0 +1,652 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func init() { + resource.AddTestSweepers("aws_imagebuilder_component", &resource.Sweeper{ + Name: "aws_imagebuilder_component", + F: testSweepImageBuilderComponents, + }) +} + +func testSweepImageBuilderComponents(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).imagebuilderconn + + var sweeperErrs *multierror.Error + + input := &imagebuilder.ListComponentsInput{ + Owner: aws.String(imagebuilder.OwnershipSelf), + } + + err = conn.ListComponentsPages(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.ListComponentBuildVersionsPages(input, func(page *imagebuilder.ListComponentBuildVersionsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, componentSummary := range page.ComponentSummaryList { + if componentSummary == nil { + continue + } + + arn := aws.StringValue(componentSummary.Arn) + + r := resourceAwsImageBuilderComponent() + d := r.Data(nil) + d.SetId(arn) + + err := r.Delete(d, client) + + if err != nil { + sweeperErr := fmt.Errorf("error deleting Image Builder Component (%s): %w", arn, err) + log.Printf("[ERROR] %s", sweeperErr) + sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) + continue + } + } + + return !lastPage + }) + + 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 + } + } + + return !lastPage + }) + + if testSweepSkipSweepError(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)) + } + + return sweeperErrs.ErrorOrNil() +} + +func TestAccAwsImageBuilderComponent_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "imagebuilder", regexp.MustCompile(fmt.Sprintf("component/%s/1.0.0/[1-9][0-9]*", rName))), + resource.TestCheckResourceAttr(resourceName, "change_description", ""), + resource.TestMatchResourceAttr(resourceName, "data", regexp.MustCompile(`schemaVersion`)), + testAccCheckResourceAttrRfc3339(resourceName, "date_created"), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "encrypted", "true"), + resource.TestCheckResourceAttr(resourceName, "kms_key_id", ""), + resource.TestCheckResourceAttr(resourceName, "name", rName), + testAccCheckResourceAttrAccountID(resourceName, "owner"), + resource.TestCheckResourceAttr(resourceName, "platform", imagebuilder.PlatformLinux), + resource.TestCheckResourceAttr(resourceName, "supported_os_versions.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "type", imagebuilder.ComponentTypeBuild), + resource.TestCheckResourceAttr(resourceName, "version", "1.0.0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsImageBuilderComponent_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsImageBuilderComponent(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAwsImageBuilderComponent_ChangeDescription(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentConfigChangeDescription(rName, "description1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "change_description", "description1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsImageBuilderComponent_Description(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentConfigDescription(rName, "description1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", "description1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsImageBuilderComponent_KmsKeyId(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + kmsKeyResourceName := "aws_kms_key.test" + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentConfigKmsKeyId(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "kms_key_id", kmsKeyResourceName, "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsImageBuilderComponent_Platform_Windows(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentConfigPlatformWindows(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "platform", imagebuilder.PlatformWindows), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsImageBuilderComponent_SupportedOsVersions(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentConfigSupportedOsVersions(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "supported_os_versions.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsImageBuilderComponent_Tags(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsImageBuilderComponentConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAwsImageBuilderComponentConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccAwsImageBuilderComponent_Uri(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_imagebuilder_component.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsImageBuilderComponentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsImageBuilderComponentConfigUri(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsImageBuilderComponentExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"uri"}, + }, + }, + }) +} + +func testAccCheckAwsImageBuilderComponentDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).imagebuilderconn + + 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.GetComponent(input) + + if tfawserr.ErrCodeEquals(err, imagebuilder.ErrCodeResourceNotFoundException) { + continue + } + + if err != nil { + return fmt.Errorf("error getting Image Builder Component (%s): %w", rs.Primary.ID, err) + } + + if output != nil { + return fmt.Errorf("Image Builder Component (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAwsImageBuilderComponentExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("resource not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).imagebuilderconn + + input := &imagebuilder.GetComponentInput{ + ComponentBuildVersionArn: aws.String(rs.Primary.ID), + } + + _, err := conn.GetComponent(input) + + if err != nil { + return fmt.Errorf("error getting Image Builder Component (%s): %w", rs.Primary.ID, err) + } + + return nil + } +} + +func testAccAwsImageBuilderComponentConfigChangeDescription(rName string, changeDescription string) string { + return fmt.Sprintf(` +resource "aws_imagebuilder_component" "test" { + change_description = %[2]q + 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, changeDescription) +} + +func testAccAwsImageBuilderComponentConfigDescription(rName string, description 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 + }) + description = %[2]q + name = %[1]q + platform = "Linux" + version = "1.0.0" +} +`, rName, description) +} + +func testAccAwsImageBuilderComponentConfigKmsKeyId(rName string) string { + return fmt.Sprintf(` +resource "aws_kms_key" "test" { + deletion_window_in_days = 7 +} + +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 + }) + kms_key_id = aws_kms_key.test.arn + name = %[1]q + platform = "Linux" + version = "1.0.0" +} +`, rName) +} + +func testAccAwsImageBuilderComponentConfigName(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 testAccAwsImageBuilderComponentConfigPlatformWindows(rName string) string { + return fmt.Sprintf(` +resource "aws_imagebuilder_component" "test" { + data = yamlencode({ + phases = [{ + name = "build" + steps = [{ + action = "ExecutePowerShell" + inputs = { + commands = ["echo 'hello world'"] + } + name = "example" + onFailure = "Continue" + }] + }] + schemaVersion = 1.0 + }) + name = %[1]q + platform = "Windows" + version = "1.0.0" +} +`, rName) +} + +func testAccAwsImageBuilderComponentConfigSupportedOsVersions(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" + supported_os_versions = ["Amazon Linux 2"] + version = "1.0.0" +} +`, rName) +} + +func testAccAwsImageBuilderComponentConfigTags1(rName string, tagKey1 string, tagValue1 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" + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAwsImageBuilderComponentConfigTags2(rName string, tagKey1 string, tagValue1 string, tagKey2 string, tagValue2 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" + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccAwsImageBuilderComponentConfigUri(rName string) string { + return fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_s3_bucket_object" "test" { + bucket = aws_s3_bucket.test.bucket + content = yamlencode({ + phases = [{ + name = "build" + steps = [{ + action = "ExecuteBash" + inputs = { + commands = ["echo 'hello world'"] + } + name = "example" + onFailure = "Continue" + }] + }] + schemaVersion = 1.0 + }) + key = "test.yml" +} + +resource "aws_imagebuilder_component" "test" { + name = %[1]q + platform = "Linux" + uri = "s3://${aws_s3_bucket.test.bucket}/${aws_s3_bucket_object.test.key}" + version = "1.0.0" +} +`, rName) +} diff --git a/website/allowed-subcategories.txt b/website/allowed-subcategories.txt index c0634642581..88fa369eeeb 100644 --- a/website/allowed-subcategories.txt +++ b/website/allowed-subcategories.txt @@ -61,6 +61,7 @@ Glue GuardDuty IAM Identity Store +Image Builder Inspector IoT KMS diff --git a/website/docs/d/imagebuilder_component.html.markdown b/website/docs/d/imagebuilder_component.html.markdown new file mode 100644 index 00000000000..13c903cb3bf --- /dev/null +++ b/website/docs/d/imagebuilder_component.html.markdown @@ -0,0 +1,41 @@ +--- +subcategory: "Image Builder" +layout: "aws" +page_title: "AWS: aws_imagebuilder_component" +description: |- + Provides details about an Image Builder Component +--- + +# Data Source: aws_imagebuilder_component + +Provides details about an Image Builder Component. + +## Example Usage + +```hcl +data "aws_imagebuilder_component" "example" { + arn = "arn:aws:imagebuilder:us-west-2:aws:component/amazon-cloudwatch-agent-linux/1.0.0" +} +``` + +## Argument Reference + +* `arn` - (Required) Amazon Resource Name (ARN) of the component. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `change_description` - Change description of the component. +* `data` - Data of the component. +* `date_created` - Date the component was created. +* `description` - Description of the component. +* `encrypted` - Encryption status of the component. +* `kms_key_id` - Amazon Resource Name (ARN) of the Key Management Service (KMS) Key used to encrypt the component. +* `name` - Name of the component. +* `owner` - Owner of the component. +* `platform` - Platform of the component. +* `supported_os_versions` - Operating Systems (OSes) supported by the component. +* `tags` - Key-value map of resource tags for the component. +* `type` - Type of the component. +* `version` - Version of the component. diff --git a/website/docs/r/imagebuilder_component.html.markdown b/website/docs/r/imagebuilder_component.html.markdown new file mode 100644 index 00000000000..0f164dc33ab --- /dev/null +++ b/website/docs/r/imagebuilder_component.html.markdown @@ -0,0 +1,86 @@ +--- +subcategory: "Image Builder" +layout: "aws" +page_title: "AWS: aws_imagebuilder_component" +description: |- + Manage an Image Builder Component +--- + +# Resource: aws_imagebuilder_component + +Manages an Image Builder Component. + +## Example Usage + +### Inline Data Document + +```hcl +resource "aws_imagebuilder_component" "example" { + data = yamlencode({ + phases = [{ + name = "build" + steps = [{ + action = "ExecuteBash" + inputs = { + commands = ["echo 'hello world'"] + } + name = "example" + onFailure = "Continue" + }] + }] + schemaVersion = 1.0 + }) + name = "example" + platform = "Linux" + version = "1.0.0" +} +``` + +### URI Document + +```hcl +resource "aws_imagebuilder_component" "example" { + name = "example" + platform = "Linux" + uri = "s3://${aws_s3_bucket_object.example.bucket}/${aws_s3_bucket_object.example.key}" + version = "1.0.0" +} +``` + +## Argument Reference + +The following arguments are required: + +* `name` - (Required) Name of the component. +* `platform` - (Required) Platform of the component. +* `version` - (Required) Version of the component. + +The following attributes are optional: + +* `change_description` - (Optional) Change description of the component. +* `data` - (Optional) Inline YAML string with data of the component. Exactly one of `data` and `uri` can be specified. Terraform will only perform drift detection of its value when present in a configuration. +* `description` - (Optional) Description of the component. +* `kms_key_id` - (Optional) Amazon Resource Name (ARN) of the Key Management Service (KMS) Key used to encrypt the component. +* `supported_os_versions` - (Optional) Set of Operating Systems (OS) supported by the component. +* `tags` - (Optional) Key-value map of resource tags for the component. +* `uri` - (Optional) S3 URI with data of the component. Exactly one of `data` and `uri` can be specified. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - (Required) Amazon Resource Name (ARN) of the component. +* `date_created` - Date the component was created. +* `encrypted` - Encryption status of the component. +* `owner` - Owner of the component. +* `type` - Type of the component. + +## Import + +`aws_imagebuilder_components` resources can be imported by using the Amazon Resource Name (ARN), e.g. + +``` +$ terraform import aws_imagebuilder_component.example arn:aws:imagebuilder:us-east-1:123456789012:component/example/1.0.0/1 +``` + +Certain resource arguments, such as `uri`, cannot be read via the API and imported into Terraform. Terraform will display a difference for these arguments the first run after import if declared in the Terraform configuration for an imported resource.