From cdbcff08fbd236943b5dca5cf0e89f4b05b57ca8 Mon Sep 17 00:00:00 2001 From: Derek Helmick Date: Wed, 17 Apr 2024 09:36:04 -0400 Subject: [PATCH 1/4] r/imagebuilder_image: adds workflow functionality --- .changelog/36953.txt | 7 + internal/service/imagebuilder/flex.go | 187 +++++++++++++ internal/service/imagebuilder/image.go | 69 +++++ internal/service/imagebuilder/image_test.go | 248 ++++++++++++++++++ .../docs/r/imagebuilder_image.html.markdown | 21 ++ 5 files changed, 532 insertions(+) create mode 100644 .changelog/36953.txt create mode 100644 internal/service/imagebuilder/flex.go diff --git a/.changelog/36953.txt b/.changelog/36953.txt new file mode 100644 index 000000000000..ee04f882c33e --- /dev/null +++ b/.changelog/36953.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_imagebuilder_image: Add `workflow` attribute +``` + +```release-note:enhancement +resource/aws_imagebuilder_image: Add `execution_role` attribute +``` diff --git a/internal/service/imagebuilder/flex.go b/internal/service/imagebuilder/flex.go new file mode 100644 index 000000000000..e42e8ac76c1a --- /dev/null +++ b/internal/service/imagebuilder/flex.go @@ -0,0 +1,187 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package imagebuilder + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/imagebuilder" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func flattenWorkflowParameter(apiObject *imagebuilder.WorkflowParameter) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + if v := apiObject.Value; v != nil { + // ImageBuilder API quirk + // Even though Value is a slice, only one element is accepted. + tfMap["value"] = aws.StringValueSlice(v)[0] + } + + return tfMap +} + +func flattenWorkflowParameters(apiObjects []*imagebuilder.WorkflowParameter) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenWorkflowParameter(apiObject)) + } + + return tfList +} + +func flattenWorkflowConfiguration(apiObject *imagebuilder.WorkflowConfiguration) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.WorkflowArn; v != nil { + tfMap["workflow_arn"] = aws.StringValue(v) + } + + if v := apiObject.OnFailure; v != nil { + tfMap["on_failure"] = aws.String(*v) + } + + if v := apiObject.ParallelGroup; v != nil { + tfMap["parallel_group"] = aws.String(*v) + } + + if v := apiObject.Parameters; v != nil { + tfMap["parameter"] = flattenWorkflowParameters(v) + } + return tfMap +} + +func flattenWorkflowConfigurations(apiObjects []*imagebuilder.WorkflowConfiguration) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObjects == nil { + continue + } + tfList = append(tfList, flattenWorkflowConfiguration(apiObject)) + } + return tfList +} + +func expandWorkflowParameter(tfMap map[string]interface{}) *imagebuilder.WorkflowParameter { + if tfMap == nil { + return nil + } + + apiObject := &imagebuilder.WorkflowParameter{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) + } + + if v, ok := tfMap["value"].(string); ok && v != "" { + // ImageBuilder API quirk + // Even though Value is a slice, only one element is accepted. + apiObject.Value = aws.StringSlice([]string{v}) + } + + return apiObject +} + +func expandWorkflowParameters(tfList []interface{}) []*imagebuilder.WorkflowParameter { + if len(tfList) == 0 { + return nil + } + + var apiObjects []*imagebuilder.WorkflowParameter + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + apiObject := expandWorkflowParameter(tfMap) + + if apiObject == nil { + continue + } + + apiObjects = append(apiObjects, apiObject) + } + + return apiObjects +} + +func expandWorkflowConfiguration(tfMap map[string]interface{}) *imagebuilder.WorkflowConfiguration { + if tfMap == nil { + return nil + } + + apiObject := &imagebuilder.WorkflowConfiguration{} + + if v, ok := tfMap["workflow_arn"].(string); ok && v != "" { + apiObject.WorkflowArn = aws.String(v) + } + + if v, ok := tfMap["on_failure"].(string); ok && v != "" { + apiObject.OnFailure = aws.String(v) + } + + if v, ok := tfMap["parallel_group"].(string); ok && v != "" { + apiObject.ParallelGroup = aws.String(v) + } + + if v, ok := tfMap["parameter"].(*schema.Set); ok && v.Len() > 0 { + apiObject.Parameters = expandWorkflowParameters(v.List()) + } + + return apiObject +} + +func expandWorkflowConfigurations(tfList []interface{}) []*imagebuilder.WorkflowConfiguration { + if len(tfList) == 0 { + return nil + } + + var apiObjects []*imagebuilder.WorkflowConfiguration + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + apiObject := expandWorkflowConfiguration(tfMap) + + if apiObject == nil { + continue + } + + apiObjects = append(apiObjects, apiObject) + } + + return apiObjects +} diff --git a/internal/service/imagebuilder/image.go b/internal/service/imagebuilder/image.go index 51a3d98b7e6d..9d0383d91852 100644 --- a/internal/service/imagebuilder/image.go +++ b/internal/service/imagebuilder/image.go @@ -68,6 +68,11 @@ func ResourceImage() *schema.Resource { ForceNew: true, Default: true, }, + "execution_role": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringMatch(regexache.MustCompile(`^(?:arn:aws(?:-[a-z]+)*:iam::[0-9]{12}:role/)?[a-zA-Z_0-9+=,.@\-_/]+$`), "valid IAM role ARN must be provided"), + }, "image_recipe_arn": { Type: schema.TypeString, Optional: true, @@ -212,6 +217,54 @@ func ResourceImage() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "workflow": { + Type: schema.TypeSet, + Computed: true, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "workflow_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch(regexache.MustCompile(`^arn:aws(?:-[a-z]+)*:imagebuilder:[a-z]{2,}(?:-[a-z]+)+-[0-9]+:(?:[0-9]{12}|aws):workflow/(build|test|distribution)/[a-z0-9-_]+/(?:(?:([0-9]+|x)\.([0-9]+|x)\.([0-9]+|x))|(?:[0-9]+\.[0-9]+\.[0-9]+/[0-9]+))$`), "valid workflow ARN must be provided"), + }, + "on_failure": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(imagebuilder.OnWorkflowFailure_Values(), false), + }, + "parallel_group": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringMatch(regexache.MustCompile(`^[A-Za-z0-9][A-Za-z0-9-_+#]{0,99}$`), "valid parallel group string must be provider"), + }, + "parameter": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "value": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + }, + }, + }, + }, + }, }, CustomizeDiff: verify.SetTagsDiff, @@ -236,6 +289,10 @@ func resourceImageCreate(ctx context.Context, d *schema.ResourceData, meta inter 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)) } @@ -252,6 +309,10 @@ func resourceImageCreate(ctx context.Context, d *schema.ResourceData, meta inter input.InfrastructureConfigurationArn = aws.String(v.(string)) } + if v, ok := d.GetOk("workflow"); ok && len(v.(*schema.Set).List()) > 0 { + input.Workflows = expandWorkflowConfigurations(v.(*schema.Set).List()) + } + output, err := conn.CreateImageWithContext(ctx, input) if err != nil { @@ -310,6 +371,8 @@ func resourceImageRead(ctx context.Context, d *schema.ResourceData, meta interfa d.Set("enhanced_image_metadata_enabled", image.EnhancedImageMetadataEnabled) + d.Set("execution_role", image.ExecutionRole) + if image.ImageRecipe != nil { d.Set("image_recipe_arn", image.ImageRecipe.Arn) } @@ -344,6 +407,12 @@ func resourceImageRead(ctx context.Context, d *schema.ResourceData, meta interfa d.Set("version", image.Version) + if image.Workflows != nil { + d.Set("workflow", flattenWorkflowConfigurations(image.Workflows)) + } else { + d.Set("workflow", nil) + } + return diags } diff --git a/internal/service/imagebuilder/image_test.go b/internal/service/imagebuilder/image_test.go index 65720fd8dcbc..1327419df062 100644 --- a/internal/service/imagebuilder/image_test.go +++ b/internal/service/imagebuilder/image_test.go @@ -320,6 +320,34 @@ func TestAccImageBuilderImage_outputResources_containers(t *testing.T) { }) } +func TestAccImageBuilderImage_workflows(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_imagebuilder_image.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.ImageBuilderServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckImageDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccImageConfig_workflows(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckImageExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "workflow.#", "2"), + resource.TestCheckResourceAttrPair(resourceName, "workflow.0.workflow_arn", "aws_imagebuilder_workflow.test_build", "arn"), + resource.TestCheckResourceAttr(resourceName, "workflow.0.parameter.#", "1"), + resource.TestCheckResourceAttr(resourceName, "workflow.0.parameter.0.name", "foo"), + resource.TestCheckResourceAttr(resourceName, "workflow.0.parameter.0.value", "bar"), + resource.TestCheckResourceAttr(resourceName, "workflow.1.on_failure", "CONTINUE"), + resource.TestCheckResourceAttr(resourceName, "workflow.1.parallel_group", "baz"), + ), + }, + }, + }) +} + func testAccCheckImageDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ImageBuilderConn(ctx) @@ -559,6 +587,226 @@ resource "aws_imagebuilder_image" "test" { `) } +func testAccImageConfig_workflows(rName string) string { + return acctest.ConfigCompose( + testAccImageBaseConfig(rName), + fmt.Sprintf(` +resource "aws_imagebuilder_workflow" "test_build" { + name = join("-", [%[1]q, "build"]) + version = "1.0.0" + type = "BUILD" + + data = <<-EOT +name: ${join("-", [%[1]q, "build"])} +description: Workflow to build an AMI +schemaVersion: 1.0 + +parameters: + - name: foo + type: string + +steps: + - name: LaunchBuildInstance + action: LaunchInstance + onFailure: Abort + inputs: + waitFor: "ssmAgent" + + - name: UpdateSSMAgent + action: RunCommand + onFailure: Abort + inputs: + documentName: "AWS-UpdateSSMAgent" + instanceId.$: "$.stepOutputs.LaunchBuildInstance.instanceId" + parameters: + allowDowngrade: + - "false" + + - name: ApplyBuildComponents + action: ExecuteComponents + onFailure: Abort + inputs: + instanceId.$: "$.stepOutputs.LaunchBuildInstance.instanceId" + + - name: InventoryCollection + action: CollectImageMetadata + onFailure: Abort + if: + and: + - stringEquals: "AMI" + value: "$.imagebuilder.imageType" + - booleanEquals: true + value: "$.imagebuilder.collectImageMetadata" + inputs: + instanceId.$: "$.stepOutputs.LaunchBuildInstance.instanceId" + + - name: RunSanitizeScript + action: SanitizeInstance + onFailure: Abort + if: + and: + - stringEquals: "AMI" + value: "$.imagebuilder.imageType" + - stringEquals: "Linux" + value: "$.imagebuilder.platform" + inputs: + instanceId.$: "$.stepOutputs.LaunchBuildInstance.instanceId" + + - name: RunSysPrepScript + action: RunSysPrep + onFailure: Abort + if: + and: + - stringEquals: "AMI" + value: "$.imagebuilder.imageType" + - stringEquals: "Windows" + value: "$.imagebuilder.platform" + inputs: + instanceId.$: "$.stepOutputs.LaunchBuildInstance.instanceId" + + - name: CreateOutputAMI + action: CreateImage + onFailure: Abort + if: + stringEquals: "AMI" + value: "$.imagebuilder.imageType" + inputs: + instanceId.$: "$.stepOutputs.LaunchBuildInstance.instanceId" + + - name: TerminateBuildInstance + action: TerminateInstance + onFailure: Continue + inputs: + instanceId.$: "$.stepOutputs.LaunchBuildInstance.instanceId" + +outputs: + - name: "ImageId" + value: "$.stepOutputs.CreateOutputAMI.imageId" + EOT +} + +resource "aws_imagebuilder_workflow" "test_test" { + name = join("-", [%[1]q, "test"]) + version = "1.0.0" + type = "TEST" + + data = <<-EOT +name: ${join("-", [%[1]q, "test"])} +description: Workflow to test an AMI +schemaVersion: 1.0 + +steps: + - name: LaunchTestInstance + action: LaunchInstance + onFailure: Abort + inputs: + waitFor: "ssmAgent" + + - name: CollectImageScanFindings + action: CollectImageScanFindings + onFailure: Continue + if: + and: + - booleanEquals: true + value: "$.imagebuilder.collectImageScanFindings" + - or: + - stringEquals: "Linux" + value: "$.imagebuilder.platform" + - stringEquals: "Windows" + value: "$.imagebuilder.platform" + inputs: + instanceId.$: "$.stepOutputs.LaunchTestInstance.instanceId" + + - name: ApplyTestComponents + action: ExecuteComponents + onFailure: Abort + inputs: + instanceId.$: "$.stepOutputs.LaunchTestInstance.instanceId" + + - name: TerminateTestInstance + action: TerminateInstance + onFailure: Continue + inputs: + instanceId.$: "$.stepOutputs.LaunchTestInstance.instanceId" + EOT +} + +data "aws_caller_identity" "current" {} + +resource "aws_iam_role" "test_execute" { + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "imagebuilder.${data.aws_partition.current.dns_suffix}" + } + Sid = "" + }] + }) + name = join("-", [%[1]q, "execute"]) +} + +data "aws_iam_policy" "AWSServiceRoleForImageBuilder" { + arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/aws-service-role/AWSServiceRoleForImageBuilder" +} + +resource "aws_iam_policy" "test_execute_service_policy" { + name = join("-", [%[1]q, "execute-service"]) + policy = data.aws_iam_policy.AWSServiceRoleForImageBuilder.policy +} + +resource "aws_iam_role_policy_attachment" "test_execute_service" { + policy_arn = aws_iam_policy.test_execute_service_policy.arn + role = aws_iam_role.test_execute.name +} + +resource "aws_iam_policy" "test_execute" { + name = join("-", [%[1]q, "execute"]) + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Action = "ssm:SendCommand" + Effect = "Allow" + Resource = "arn:${data.aws_partition.current.partition}:ssm:${data.aws_region.current.id}::document/AWS-UpdateSSMAgent" + }] + }) +} + +resource "aws_iam_role_policy_attachment" "test_execute" { + policy_arn = aws_iam_policy.test_execute.arn + role = aws_iam_role.test_execute.name +} + +resource "aws_imagebuilder_image" "test" { + image_recipe_arn = aws_imagebuilder_image_recipe.test.arn + infrastructure_configuration_arn = aws_imagebuilder_infrastructure_configuration.test.arn + execution_role = aws_iam_role.test_execute.arn + + workflow { + workflow_arn = aws_imagebuilder_workflow.test_build.arn + + parameter { + name = "foo" + value = "bar" + } + } + + workflow { + workflow_arn = aws_imagebuilder_workflow.test_test.arn + on_failure = "CONTINUE" + parallel_group = "baz" + } + + depends_on = [ + aws_iam_role_policy_attachment.test_execute, + aws_iam_role_policy_attachment.test_execute_service + ] +} +`, rName), + ) +} func testAccImageConfig_tags1(rName string, tagKey1 string, tagValue1 string) string { return acctest.ConfigCompose( testAccImageBaseConfig(rName), diff --git a/website/docs/r/imagebuilder_image.html.markdown b/website/docs/r/imagebuilder_image.html.markdown index 0314e4cc0429..87f3b1700e03 100644 --- a/website/docs/r/imagebuilder_image.html.markdown +++ b/website/docs/r/imagebuilder_image.html.markdown @@ -31,9 +31,11 @@ The following arguments are optional: * `container_recipe_arn` - (Optional) - Amazon Resource Name (ARN) of the container recipe. * `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_tests_configuration` - (Optional) Configuration block with image tests configuration. Detailed below. * `image_scanning_configuration` - (Optional) Configuration block with image scanning configuration. Detailed below. +* `workflow` - (Optional) Configuration block with the workflow configuration. Detailed below. * `tags` - (Optional) Key-value map of resource tags for the Image Builder Image. 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_tests_configuration @@ -57,6 +59,25 @@ The following arguments are optional: * `repository_name` - (Optional) The name of the container repository that Amazon Inspector scans to identify findings for your container images. * `container_tags` - (Optional) Set of tags for Image Builder to apply to the output container image that that Amazon Inspector scans. +### workflow + +The following arguments are required: + +* `workflow_arn` - (Required) Amaozon 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: From d26c32d26a207290c2f88a3b01bc8f715bee8752 Mon Sep 17 00:00:00 2001 From: Derek Helmick Date: Wed, 17 Apr 2024 18:30:37 -0400 Subject: [PATCH 2/4] r/imagebuilder_image: spelling issue --- website/docs/r/imagebuilder_image.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/imagebuilder_image.html.markdown b/website/docs/r/imagebuilder_image.html.markdown index 87f3b1700e03..e7df580fd6b0 100644 --- a/website/docs/r/imagebuilder_image.html.markdown +++ b/website/docs/r/imagebuilder_image.html.markdown @@ -63,7 +63,7 @@ The following arguments are optional: The following arguments are required: -* `workflow_arn` - (Required) Amaozon Resource Name (ARN) of the Image Builder Workflow. +* `workflow_arn` - (Required) Amazon Resource Name (ARN) of the Image Builder Workflow. The following arguments are optional: From c1fac152426d92183a5d2e03d8b0625e0454fe27 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Apr 2024 07:53:37 -0400 Subject: [PATCH 3/4] Cosmetics. --- .changelog/36953.txt | 6 +----- internal/service/imagebuilder/flex.go | 19 +++++++++++-------- internal/service/imagebuilder/image.go | 22 +++++++++++----------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/.changelog/36953.txt b/.changelog/36953.txt index ee04f882c33e..41b85b957363 100644 --- a/.changelog/36953.txt +++ b/.changelog/36953.txt @@ -1,7 +1,3 @@ ```release-note:enhancement -resource/aws_imagebuilder_image: Add `workflow` attribute -``` - -```release-note:enhancement -resource/aws_imagebuilder_image: Add `execution_role` attribute +resource/aws_imagebuilder_image: Add `execution_role` and `workflow` arguments ``` diff --git a/internal/service/imagebuilder/flex.go b/internal/service/imagebuilder/flex.go index e42e8ac76c1a..193ed88f22b3 100644 --- a/internal/service/imagebuilder/flex.go +++ b/internal/service/imagebuilder/flex.go @@ -54,10 +54,6 @@ func flattenWorkflowConfiguration(apiObject *imagebuilder.WorkflowConfiguration) tfMap := map[string]interface{}{} - if v := apiObject.WorkflowArn; v != nil { - tfMap["workflow_arn"] = aws.StringValue(v) - } - if v := apiObject.OnFailure; v != nil { tfMap["on_failure"] = aws.String(*v) } @@ -69,6 +65,11 @@ func flattenWorkflowConfiguration(apiObject *imagebuilder.WorkflowConfiguration) if v := apiObject.Parameters; v != nil { tfMap["parameter"] = flattenWorkflowParameters(v) } + + if v := apiObject.WorkflowArn; v != nil { + tfMap["workflow_arn"] = aws.StringValue(v) + } + return tfMap } @@ -83,8 +84,10 @@ func flattenWorkflowConfigurations(apiObjects []*imagebuilder.WorkflowConfigurat if apiObjects == nil { continue } + tfList = append(tfList, flattenWorkflowConfiguration(apiObject)) } + return tfList } @@ -141,10 +144,6 @@ func expandWorkflowConfiguration(tfMap map[string]interface{}) *imagebuilder.Wor apiObject := &imagebuilder.WorkflowConfiguration{} - if v, ok := tfMap["workflow_arn"].(string); ok && v != "" { - apiObject.WorkflowArn = aws.String(v) - } - if v, ok := tfMap["on_failure"].(string); ok && v != "" { apiObject.OnFailure = aws.String(v) } @@ -157,6 +156,10 @@ func expandWorkflowConfiguration(tfMap map[string]interface{}) *imagebuilder.Wor apiObject.Parameters = expandWorkflowParameters(v.List()) } + if v, ok := tfMap["workflow_arn"].(string); ok && v != "" { + apiObject.WorkflowArn = aws.String(v) + } + return apiObject } diff --git a/internal/service/imagebuilder/image.go b/internal/service/imagebuilder/image.go index 9d0383d91852..356273438bbe 100644 --- a/internal/service/imagebuilder/image.go +++ b/internal/service/imagebuilder/image.go @@ -49,7 +49,7 @@ func ResourceImage() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validation.StringMatch(regexache.MustCompile(`^arn:aws[^:]*:imagebuilder:[^:]+:(?:\d{12}|aws):container-recipe/[0-9a-z_-]+/\d+\.\d+\.\d+$`), "valid container recipe ARN must be provided"), + ValidateFunc: verify.ValidARN, ExactlyOneOf: []string{"container_recipe_arn", "image_recipe_arn"}, }, "date_created": { @@ -60,7 +60,7 @@ func ResourceImage() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validation.StringMatch(regexache.MustCompile(`^arn:aws[^:]*:imagebuilder:[^:]+:(?:\d{12}|aws):distribution-configuration/[0-9a-z_-]+$`), "valid distribution configuration ARN must be provided"), + ValidateFunc: verify.ValidARN, }, "enhanced_image_metadata_enabled": { Type: schema.TypeBool, @@ -71,13 +71,13 @@ func ResourceImage() *schema.Resource { "execution_role": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.StringMatch(regexache.MustCompile(`^(?:arn:aws(?:-[a-z]+)*:iam::[0-9]{12}:role/)?[a-zA-Z_0-9+=,.@\-_/]+$`), "valid IAM role ARN must be provided"), + ValidateFunc: verify.ValidARN, }, "image_recipe_arn": { Type: schema.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validation.StringMatch(regexache.MustCompile(`^arn:aws[^:]*:imagebuilder:[^:]+:(?:\d{12}|aws):image-recipe/[0-9a-z_-]+/\d+\.\d+\.\d+$`), "valid image recipe ARN must be provided"), + ValidateFunc: verify.ValidARN, ExactlyOneOf: []string{"container_recipe_arn", "image_recipe_arn"}, }, "image_scanning_configuration": { @@ -144,7 +144,7 @@ func ResourceImage() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringMatch(regexache.MustCompile(`^arn:aws[^:]*:imagebuilder:[^:]+:(?:\d{12}|aws):infrastructure-configuration/[0-9a-z_-]+$`), "valid infrastructure configuration ARN must be provided"), + ValidateFunc: verify.ValidARN, }, "name": { Type: schema.TypeString, @@ -224,12 +224,6 @@ func ResourceImage() *schema.Resource { ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "workflow_arn": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringMatch(regexache.MustCompile(`^arn:aws(?:-[a-z]+)*:imagebuilder:[a-z]{2,}(?:-[a-z]+)+-[0-9]+:(?:[0-9]{12}|aws):workflow/(build|test|distribution)/[a-z0-9-_]+/(?:(?:([0-9]+|x)\.([0-9]+|x)\.([0-9]+|x))|(?:[0-9]+\.[0-9]+\.[0-9]+/[0-9]+))$`), "valid workflow ARN must be provided"), - }, "on_failure": { Type: schema.TypeString, Optional: true, @@ -262,6 +256,12 @@ func ResourceImage() *schema.Resource { }, }, }, + "workflow_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: verify.ValidARN, + }, }, }, }, From c249f585858a6da56754d50e740c00de1e8291f5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Apr 2024 08:35:59 -0400 Subject: [PATCH 4/4] Add 'Computed' to 'execution_role'. --- internal/service/imagebuilder/image.go | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/internal/service/imagebuilder/image.go b/internal/service/imagebuilder/image.go index 356273438bbe..ec3d38664ab1 100644 --- a/internal/service/imagebuilder/image.go +++ b/internal/service/imagebuilder/image.go @@ -71,6 +71,7 @@ func ResourceImage() *schema.Resource { "execution_role": { Type: schema.TypeString, Optional: true, + Computed: true, ValidateFunc: verify.ValidARN, }, "image_recipe_arn": { @@ -359,60 +360,48 @@ func resourceImageRead(ctx context.Context, d *schema.ResourceData, meta interfa image := output.Image d.Set("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) - d.Set("execution_role", image.ExecutionRole) - 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)}) } else { d.Set("image_scanning_configuration", nil) } - if image.ImageTestsConfiguration != nil { d.Set("image_tests_configuration", []interface{}{flattenImageTestsConfiguration(image.ImageTestsConfiguration)}) } else { d.Set("image_tests_configuration", nil) } - if image.InfrastructureConfiguration != nil { d.Set("infrastructure_configuration_arn", image.InfrastructureConfiguration.Arn) } - d.Set("name", 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)}) } else { d.Set("output_resources", nil) } - - setTagsOut(ctx, image.Tags) - + d.Set("platform", image.Platform) d.Set("version", image.Version) - if image.Workflows != nil { d.Set("workflow", flattenWorkflowConfigurations(image.Workflows)) } else { d.Set("workflow", nil) } + setTagsOut(ctx, image.Tags) + return diags }