diff --git a/.changelog/37317.txt b/.changelog/37317.txt new file mode 100644 index 000000000000..1476b8fcb6b4 --- /dev/null +++ b/.changelog/37317.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_imagebuilder_image_pipeline: Add `execution_role` and `workflow` arguments +``` diff --git a/internal/service/imagebuilder/image_pipeline.go b/internal/service/imagebuilder/image_pipeline.go index e7b3adab89c8..174bca68b698 100644 --- a/internal/service/imagebuilder/image_pipeline.go +++ b/internal/service/imagebuilder/image_pipeline.go @@ -78,6 +78,11 @@ func ResourceImagePipeline() *schema.Resource { Optional: true, Default: true, }, + "execution_role": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: verify.ValidARN, + }, "image_recipe_arn": { Type: schema.TypeString, Optional: true, @@ -193,6 +198,47 @@ func ResourceImagePipeline() *schema.Resource { }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), + "workflow": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "on_failure": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(imagebuilder.OnWorkflowFailure_Values(), false), + }, + "parallel_group": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 100), + }, + names.AttrParameter: { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrName: { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + names.AttrValue: { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "workflow_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, + }, + }, + }, + }, }, CustomizeDiff: verify.SetTagsDiff, @@ -221,6 +267,10 @@ func resourceImagePipelineCreate(ctx context.Context, d *schema.ResourceData, me input.DistributionConfigurationArn = aws.String(v.(string)) } + if v, ok := d.GetOk("execution_role"); ok { + input.ExecutionRole = aws.String(v.(string)) + } + if v, ok := d.GetOk("image_recipe_arn"); ok { input.ImageRecipeArn = aws.String(v.(string)) } @@ -249,6 +299,10 @@ func resourceImagePipelineCreate(ctx context.Context, d *schema.ResourceData, me input.Status = aws.String(v.(string)) } + if v, ok := d.GetOk("workflow"); ok && len(v.([]interface{})) > 0 { + input.Workflows = expandWorkflowConfigurations(v.([]interface{})) + } + output, err := conn.CreateImagePipelineWithContext(ctx, input) if err != nil { @@ -299,6 +353,7 @@ func resourceImagePipelineRead(ctx context.Context, d *schema.ResourceData, meta d.Set(names.AttrDescription, imagePipeline.Description) d.Set("distribution_configuration_arn", imagePipeline.DistributionConfigurationArn) d.Set("enhanced_image_metadata_enabled", imagePipeline.EnhancedImageMetadataEnabled) + 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)}) @@ -319,6 +374,7 @@ func resourceImagePipelineRead(ctx context.Context, d *schema.ResourceData, meta d.Set(names.AttrSchedule, nil) } d.Set(names.AttrStatus, imagePipeline.Status) + d.Set("workflow", flattenWorkflowConfigurations(imagePipeline.Workflows)) setTagsOut(ctx, imagePipeline.Tags) @@ -333,11 +389,13 @@ func resourceImagePipelineUpdate(ctx context.Context, d *schema.ResourceData, me names.AttrDescription, "distribution_configuration_arn", "enhanced_image_metadata_enabled", + "execution_role", "image_scanning_configuration", "image_tests_configuration", "infrastructure_configuration_arn", names.AttrSchedule, names.AttrStatus, + "workflow", ) { input := &imagebuilder.UpdateImagePipelineInput{ ClientToken: aws.String(id.UniqueId()), @@ -357,6 +415,10 @@ func resourceImagePipelineUpdate(ctx context.Context, d *schema.ResourceData, me input.DistributionConfigurationArn = aws.String(v.(string)) } + if v, ok := d.GetOk("execution_role"); ok { + input.ExecutionRole = aws.String(v.(string)) + } + if v, ok := d.GetOk("image_recipe_arn"); ok { input.ImageRecipeArn = aws.String(v.(string)) } @@ -381,6 +443,10 @@ func resourceImagePipelineUpdate(ctx context.Context, d *schema.ResourceData, me input.Status = aws.String(v.(string)) } + if v, ok := d.GetOk("workflow"); ok && len(v.([]interface{})) > 0 { + input.Workflows = expandWorkflowConfigurations(v.([]interface{})) + } + _, err := conn.UpdateImagePipelineWithContext(ctx, input) if err != nil { diff --git a/internal/service/imagebuilder/image_pipeline_test.go b/internal/service/imagebuilder/image_pipeline_test.go index 3b7311cbeb81..61fd37dea1b0 100644 --- a/internal/service/imagebuilder/image_pipeline_test.go +++ b/internal/service/imagebuilder/image_pipeline_test.go @@ -644,6 +644,80 @@ func TestAccImageBuilderImagePipeline_tags(t *testing.T) { }) } +func TestAccImageBuilderImagePipeline_workflow(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_imagebuilder_image_pipeline.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ImageBuilderServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckImagePipelineDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccImagePipelineConfig_workflow(rName, imagebuilder.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.parallel_group", "test1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccImagePipelineConfig_workflow(rName, imagebuilder.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.parallel_group", "test2"), + ), + }, + }, + }) +} + +func TestAccImageBuilderImagePipeline_workflowParameter(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_imagebuilder_image_pipeline.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ImageBuilderServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckImagePipelineDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccImagePipelineConfig_workflowParameter(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckImagePipelineExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "workflow.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "workflow.0.parameter.#", acctest.Ct1), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccImagePipelineConfig_workflowParameter(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckImagePipelineExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "workflow.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "workflow.0.parameter.#", acctest.Ct1), + ), + }, + }, + }) +} + func testAccCheckImagePipelineDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) @@ -705,6 +779,8 @@ data "aws_region" "current" {} data "aws_partition" "current" {} +data "aws_caller_identity" "current" {} + resource "aws_iam_instance_profile" "test" { name = aws_iam_role.role.name role = aws_iam_role.role.name @@ -1105,3 +1181,104 @@ resource "aws_imagebuilder_image_pipeline" "test" { } `, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } + +func testAccImagePipelineConfig_workflow(rName, onFailure, parallelGroup string) string { + return acctest.ConfigCompose(testAccImagePipelineConfig_base(rName), fmt.Sprintf(` +resource "aws_imagebuilder_workflow" "test" { + name = %[1]q + version = "1.0.0" + type = "TEST" + + data = <<-EOT + name: test-image + description: Workflow to test an image + schemaVersion: 1.0 + + steps: + - name: LaunchTestInstance + action: LaunchInstance + onFailure: Abort + inputs: + waitFor: "ssmAgent" + + - name: TerminateTestInstance + action: TerminateInstance + onFailure: Continue + inputs: + instanceId.$: "$.stepOutputs.LaunchTestInstance.instanceId" + EOT +} + +resource "aws_imagebuilder_image_pipeline" "test" { + image_recipe_arn = aws_imagebuilder_image_recipe.test.arn + infrastructure_configuration_arn = aws_imagebuilder_infrastructure_configuration.test.arn + name = %[1]q + + execution_role = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/imagebuilder.amazonaws.com/AWSServiceRoleForImageBuilder" + + workflow { + on_failure = %[2]q + parallel_group = %[3]q + workflow_arn = aws_imagebuilder_workflow.test.arn + } +} +`, rName, onFailure, parallelGroup)) +} + +func testAccImagePipelineConfig_workflowParameter(rName string) string { + return acctest.ConfigCompose(testAccImagePipelineConfig_base(rName), fmt.Sprintf(` +resource "aws_imagebuilder_workflow" "test" { + name = %[1]q + version = "1.0.0" + type = "TEST" + + data = <<-EOT + name: test-image + description: Workflow to test an image + schemaVersion: 1.0 + + parameters: + - name: waitForActionAtEnd + type: boolean + + steps: + - name: LaunchTestInstance + action: LaunchInstance + onFailure: Abort + inputs: + waitFor: "ssmAgent" + + - name: TerminateTestInstance + action: TerminateInstance + onFailure: Continue + inputs: + instanceId.$: "$.stepOutputs.LaunchTestInstance.instanceId" + + - name: WaitForActionAtEnd + action: WaitForAction + if: + booleanEquals: true + value: "$.parameters.waitForActionAtEnd" + EOT +} + +resource "aws_imagebuilder_image_pipeline" "test" { + image_recipe_arn = aws_imagebuilder_image_recipe.test.arn + infrastructure_configuration_arn = aws_imagebuilder_infrastructure_configuration.test.arn + name = %[1]q + + execution_role = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/imagebuilder.amazonaws.com/AWSServiceRoleForImageBuilder" + + workflow { + on_failure = "ABORT" + parallel_group = "test" + workflow_arn = aws_imagebuilder_workflow.test.arn + + parameter { + name = "waitForActionAtEnd" + value = "true" + } + } +} +`, rName)) +} diff --git a/website/docs/r/imagebuilder_image_pipeline.html.markdown b/website/docs/r/imagebuilder_image_pipeline.html.markdown index 21a064637fd3..4907a4e371af 100644 --- a/website/docs/r/imagebuilder_image_pipeline.html.markdown +++ b/website/docs/r/imagebuilder_image_pipeline.html.markdown @@ -37,11 +37,13 @@ The following arguments are optional: * `description` - (Optional) Description of the image pipeline. * `distribution_configuration_arn` - (Optional) Amazon Resource Name (ARN) of the Image Builder Distribution Configuration. * `enhanced_image_metadata_enabled` - (Optional) Whether additional information about the image being created is collected. Defaults to `true`. +* `execution_role` - (Optional) Amazon Resource Name (ARN) of the service-linked role to be used by Image Builder to [execute workflows](https://docs.aws.amazon.com/imagebuilder/latest/userguide/manage-image-workflows.html). * `image_recipe_arn` - (Optional) Amazon Resource Name (ARN) of the image recipe. * `image_scanning_configuration` - (Optional) Configuration block with image scanning configuration. Detailed below. * `image_tests_configuration` - (Optional) Configuration block with image tests configuration. Detailed below. * `schedule` - (Optional) Configuration block with schedule settings. Detailed below. * `status` - (Optional) Status of the image pipeline. Valid values are `DISABLED` and `ENABLED`. Defaults to `ENABLED`. +* `workflow` - (Optional) Configuration block with the workflow configuration. Detailed below. * `tags` - (Optional) Key-value map of resource tags for the image pipeline. 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. ### image_scanning_configuration @@ -77,6 +79,25 @@ The following arguments are optional: * `timezone` - (Optional) The timezone that applies to the scheduling expression. For example, "Etc/UTC", "America/Los_Angeles" in the [IANA timezone format](https://www.joda.org/joda-time/timezones.html). If not specified this defaults to UTC. +### workflow + +The following arguments are required: + +* `workflow_arn` - (Required) Amazon Resource Name (ARN) of the Image Builder Workflow. + +The following arguments are optional: + +* `on_failure` - (Optional) The action to take if the workflow fails. Must be one of `CONTINUE` or `ABORT`. +* `parallel_group` - (Optional) The parallel group in which to run a test Workflow. +* `parameter` - (Optional) Configuration block for the workflow parameters. Detailed below. + +### parameter + +The following arguments are required: + +* `name` - (Required) The name of the Workflow parameter. +* `value` - (Required) The value of the Workflow parameter. + ## Attribute Reference This resource exports the following attributes in addition to the arguments above: