diff --git a/.changelog/21565.txt b/.changelog/21565.txt new file mode 100644 index 00000000000..9311d393334 --- /dev/null +++ b/.changelog/21565.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_batch_compute_environment: Add `ec2_configuration` argument to `compute_resources` configuration block +``` diff --git a/internal/service/batch/compute_environment.go b/internal/service/batch/compute_environment.go index de2d4ff4df5..b8a149e30af 100644 --- a/internal/service/batch/compute_environment.go +++ b/internal/service/batch/compute_environment.go @@ -35,6 +35,10 @@ func ResourceComputeEnvironment() *schema.Resource { ), Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, "compute_environment_name": { Type: schema.TypeString, Optional: true, @@ -78,6 +82,30 @@ func ResourceComputeEnvironment() *schema.Resource { Optional: true, Computed: true, }, + "ec2_configuration": { + Type: schema.TypeList, + Optional: true, + Computed: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "image_id_override": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + "image_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + }, + }, + }, "ec2_key_pair": { Type: schema.TypeString, Optional: true, @@ -164,6 +192,10 @@ func ResourceComputeEnvironment() *schema.Resource { }, }, }, + "ecs_cluster_arn": { + Type: schema.TypeString, + Computed: true, + }, "service_role": { Type: schema.TypeString, Optional: true, @@ -179,6 +211,14 @@ func ResourceComputeEnvironment() *schema.Resource { ValidateFunc: validation.StringInSlice(batch.CEState_Values(), true), Default: batch.CEStateEnabled, }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "status_reason": { + Type: schema.TypeString, + Computed: true, + }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), "type": { @@ -190,22 +230,6 @@ func ResourceComputeEnvironment() *schema.Resource { }, ValidateFunc: validation.StringInSlice(batch.CEType_Values(), true), }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "ecs_cluster_arn": { - Type: schema.TypeString, - Computed: true, - }, - "status": { - Type: schema.TypeString, - Computed: true, - }, - "status_reason": { - Type: schema.TypeString, - Computed: true, - }, }, } } @@ -461,6 +485,10 @@ func expandBatchComputeResource(tfMap map[string]interface{}) *batch.ComputeReso apiObject.DesiredvCpus = aws.Int64(int64(v)) } + if v, ok := tfMap["ec2_configuration"].([]interface{}); ok && len(v) > 0 { + apiObject.Ec2Configuration = expandBatchEc2Configurations(v) + } + if v, ok := tfMap["ec2_key_pair"].(string); ok && v != "" { apiObject.Ec2KeyPair = aws.String(v) } @@ -514,6 +542,50 @@ func expandBatchComputeResource(tfMap map[string]interface{}) *batch.ComputeReso return apiObject } +func expandBatchEc2Configuration(tfMap map[string]interface{}) *batch.Ec2Configuration { + if tfMap == nil { + return nil + } + + apiObject := &batch.Ec2Configuration{} + + if v, ok := tfMap["image_id_override"].(string); ok && v != "" { + apiObject.ImageIdOverride = aws.String(v) + } + + if v, ok := tfMap["image_type"].(string); ok && v != "" { + apiObject.ImageType = aws.String(v) + } + + return apiObject +} + +func expandBatchEc2Configurations(tfList []interface{}) []*batch.Ec2Configuration { + if len(tfList) == 0 { + return nil + } + + var apiObjects []*batch.Ec2Configuration + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + apiObject := expandBatchEc2Configuration(tfMap) + + if apiObject == nil { + continue + } + + apiObjects = append(apiObjects, apiObject) + } + + return apiObjects +} + func expandBatchLaunchTemplateSpecification(tfMap map[string]interface{}) *batch.LaunchTemplateSpecification { if tfMap == nil { return nil @@ -555,6 +627,10 @@ func flattenBatchComputeResource(apiObject *batch.ComputeResource) map[string]in tfMap["desired_vcpus"] = aws.Int64Value(v) } + if v := apiObject.Ec2Configuration; v != nil { + tfMap["ec2_configuration"] = flattenBatchEc2Configurations(v) + } + if v := apiObject.Ec2KeyPair; v != nil { tfMap["ec2_key_pair"] = aws.StringValue(v) } @@ -606,6 +682,42 @@ func flattenBatchComputeResource(apiObject *batch.ComputeResource) map[string]in return tfMap } +func flattenBatchEc2Configuration(apiObject *batch.Ec2Configuration) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.ImageIdOverride; v != nil { + tfMap["image_id_override"] = aws.StringValue(v) + } + + if v := apiObject.ImageType; v != nil { + tfMap["image_type"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenBatchEc2Configurations(apiObjects []*batch.Ec2Configuration) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenBatchEc2Configuration(apiObject)) + } + + return tfList +} + func flattenBatchLaunchTemplateSpecification(apiObject *batch.LaunchTemplateSpecification) map[string]interface{} { if apiObject == nil { return nil diff --git a/internal/service/batch/compute_environment_test.go b/internal/service/batch/compute_environment_test.go index 60a26802ead..637f85958c9 100644 --- a/internal/service/batch/compute_environment_test.go +++ b/internal/service/batch/compute_environment_test.go @@ -154,6 +154,8 @@ func TestAccBatchComputeEnvironment_createEC2(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "compute_resources.0.allocation_strategy", ""), resource.TestCheckResourceAttr(resourceName, "compute_resources.0.bid_percentage", "0"), resource.TestCheckResourceAttr(resourceName, "compute_resources.0.desired_vcpus", "0"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.ec2_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.ec2_configuration.0.image_type", "ECS_AL2"), resource.TestCheckResourceAttr(resourceName, "compute_resources.0.ec2_key_pair", ""), resource.TestCheckResourceAttr(resourceName, "compute_resources.0.image_id", ""), resource.TestCheckResourceAttrPair(resourceName, "compute_resources.0.instance_role", instanceProfileResourceName, "arn"), @@ -953,6 +955,68 @@ func TestAccBatchComputeEnvironment_ComputeResources_maxVCPUs(t *testing.T) { }) } +func TestAccBatchComputeEnvironment_ec2Configuration(t *testing.T) { + var ce batch.ComputeEnvironmentDetail + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_batch_compute_environment.test" + instanceProfileResourceName := "aws_iam_instance_profile.ecs_instance" + securityGroupResourceName := "aws_security_group.test" + serviceRoleResourceName := "aws_iam_role.batch_service" + spotFleetRoleResourceName := "aws_iam_role.ec2_spot_fleet" + subnetResourceName := "aws_subnet.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, batch.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckBatchComputeEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeEnvironmentEC2Configuration(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeEnvironmentExists(resourceName, &ce), + acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "batch", fmt.Sprintf("compute-environment/%s", rName)), + resource.TestCheckResourceAttr(resourceName, "compute_environment_name", rName), + resource.TestCheckResourceAttr(resourceName, "compute_environment_name_prefix", ""), + resource.TestCheckResourceAttr(resourceName, "compute_resources.#", "1"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.allocation_strategy", ""), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.bid_percentage", "0"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.desired_vcpus", "0"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.ec2_key_pair", ""), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.image_id", ""), + resource.TestCheckResourceAttrPair(resourceName, "compute_resources.0.instance_role", instanceProfileResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.instance_type.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "compute_resources.0.instance_type.*", "optimal"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.ec2_configuration.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "compute_resources.0.ec2_configuration.0.image_id_override"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.ec2_configuration.0.image_type", "ECS_AL2"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.max_vcpus", "16"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.min_vcpus", "0"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "compute_resources.0.security_group_ids.*", securityGroupResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "compute_resources.0.spot_iam_fleet_role", spotFleetRoleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.subnets.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "compute_resources.0.subnets.*", subnetResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "compute_resources.0.type", "SPOT"), + resource.TestCheckResourceAttrSet(resourceName, "ecs_cluster_arn"), + resource.TestCheckResourceAttrPair(resourceName, "service_role", serviceRoleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "state", "ENABLED"), + resource.TestCheckResourceAttrSet(resourceName, "status"), + resource.TestCheckResourceAttrSet(resourceName, "status_reason"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "type", "MANAGED"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccBatchComputeEnvironment_launchTemplate(t *testing.T) { var ce batch.ComputeEnvironmentDetail rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -2080,3 +2144,39 @@ resource "aws_batch_compute_environment" "test" { } `, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } + +func testAccComputeEnvironmentEC2Configuration(rName string) string { + return acctest.ConfigCompose( + testAccComputeEnvironmentBaseConfig(rName), + acctest.ConfigLatestAmazonLinuxHvmEbsAmi(), + fmt.Sprintf(` +resource "aws_batch_compute_environment" "test" { + compute_environment_name = %[1]q + + compute_resources { + instance_role = aws_iam_instance_profile.ecs_instance.arn + instance_type = ["optimal"] + ec2_configuration { + image_id_override = data.aws_ami.amzn-ami-minimal-hvm-ebs.id + image_type = "ECS_AL2" + } + + max_vcpus = 16 + min_vcpus = 0 + + security_group_ids = [ + aws_security_group.test.id + ] + spot_iam_fleet_role = aws_iam_role.ec2_spot_fleet.arn + subnets = [ + aws_subnet.test.id + ] + type = "SPOT" + } + + service_role = aws_iam_role.batch_service.arn + type = "MANAGED" + depends_on = [aws_iam_role_policy_attachment.batch_service] +} +`, rName)) +} diff --git a/website/docs/r/batch_compute_environment.html.markdown b/website/docs/r/batch_compute_environment.html.markdown index 7837af8d937..d99b9f6d1f7 100644 --- a/website/docs/r/batch_compute_environment.html.markdown +++ b/website/docs/r/batch_compute_environment.html.markdown @@ -165,8 +165,9 @@ resource "aws_batch_compute_environment" "sample" { * `allocation_strategy` - (Optional) The allocation strategy to use for the compute resource in case not enough instances of the best fitting instance type can be allocated. Valid items are `BEST_FIT_PROGRESSIVE`, `SPOT_CAPACITY_OPTIMIZED` or `BEST_FIT`. Defaults to `BEST_FIT`. See [AWS docs](https://docs.aws.amazon.com/batch/latest/userguide/allocation-strategies.html) for details. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. * `bid_percentage` - (Optional) Integer of minimum percentage that a Spot Instance price must be when compared with the On-Demand price for that instance type before instances are launched. For example, if your bid percentage is 20% (`20`), then the Spot price must be below 20% of the current On-Demand price for that EC2 instance. This parameter is required for SPOT compute environments. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. * `desired_vcpus` - (Optional) The desired number of EC2 vCPUS in the compute environment. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. +* `ec2_configuration` - (Optional) Provides information used to select Amazon Machine Images (AMIs) for EC2 instances in the compute environment. If Ec2Configuration isn't specified, the default is ECS_AL2. This parameter isn't applicable to jobs that are running on Fargate resources, and shouldn't be specified. * `ec2_key_pair` - (Optional) The EC2 key pair that is used for instances launched in the compute environment. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. -* `image_id` - (Optional) The Amazon Machine Image (AMI) ID used for instances launched in the compute environment. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. +* `image_id` - (Optional) The Amazon Machine Image (AMI) ID used for instances launched in the compute environment. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. (Deprecated, use [`image_id_override`](#image_id_override) instead) * `instance_role` - (Optional) The Amazon ECS instance role applied to Amazon EC2 instances in a compute environment. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. * `instance_type` - (Optional) A list of instance types that may be launched. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. * `launch_template` - (Optional) The launch template to use for your compute resources. See details below. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. @@ -178,6 +179,13 @@ resource "aws_batch_compute_environment" "sample" { * `tags` - (Optional) Key-value pair tags to be applied to resources that are launched in the compute environment. This parameter isn't applicable to jobs running on Fargate resources, and shouldn't be specified. * `type` - (Required) The type of compute environment. Valid items are `EC2`, `SPOT`, `FARGATE` or `FARGATE_SPOT`. +### ec2_configuration + +`ec2_configuration` supports the following: + +* `image_id_override` - (Optional) The AMI ID used for instances launched in the compute environment that match the image type. This setting overrides the [`image_id` argument](#image_id) in the `compute_resourcess block. +* `image_type` - (Optional) The image type to match with the instance type to select an AMI. If the `image_id_override` parameter isn't specified, then a recent [Amazon ECS-optimized Amazon Linux 2 AMI](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html#al2ami) (`ECS_AL2`) is used. + ### launch_template `launch_template` supports the following: