Skip to content

Commit

Permalink
r/imagebuilder_image: adds workflow functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
thatderek committed Apr 17, 2024
1 parent 9316322 commit cdbcff0
Show file tree
Hide file tree
Showing 5 changed files with 532 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .changelog/36953.txt
Original file line number Diff line number Diff line change
@@ -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
```
187 changes: 187 additions & 0 deletions internal/service/imagebuilder/flex.go
Original file line number Diff line number Diff line change
@@ -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
}
69 changes: 69 additions & 0 deletions internal/service/imagebuilder/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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))
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
}

Expand Down
Loading

0 comments on commit cdbcff0

Please sign in to comment.