diff --git a/aws/data_source_aws_launch_template.go b/aws/data_source_aws_launch_template.go index 3c4f27dad4e..12e63e32ec1 100644 --- a/aws/data_source_aws_launch_template.go +++ b/aws/data_source_aws_launch_template.go @@ -79,6 +79,10 @@ func dataSourceAwsLaunchTemplate() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "throughput": { + Type: schema.TypeInt, + Computed: true, + }, "volume_size": { Type: schema.TypeInt, Computed: true, diff --git a/aws/resource_aws_launch_template.go b/aws/resource_aws_launch_template.go index 9b41755ab3c..96662f9b5ae 100644 --- a/aws/resource_aws_launch_template.go +++ b/aws/resource_aws_launch_template.go @@ -133,6 +133,12 @@ func resourceAwsLaunchTemplate() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "throughput": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + ValidateFunc: validation.IntBetween(125, 1000), + }, "volume_size": { Type: schema.TypeInt, Optional: true, @@ -929,6 +935,9 @@ func getBlockDeviceMappings(m []*ec2.LaunchTemplateBlockDeviceMapping) []interfa if v.Ebs.SnapshotId != nil { ebs["snapshot_id"] = aws.StringValue(v.Ebs.SnapshotId) } + if v.Ebs.Throughput != nil { + ebs["throughput"] = aws.Int64Value(v.Ebs.Throughput) + } mapping["ebs"] = []interface{}{ebs} } @@ -1496,6 +1505,10 @@ func readEbsBlockDeviceFromConfig(ebs map[string]interface{}) (*ec2.LaunchTempla ebsDevice.SnapshotId = aws.String(v) } + if v := ebs["throughput"].(int); v > 0 { + ebsDevice.Throughput = aws.Int64(int64(v)) + } + if v := ebs["volume_size"]; v != nil { ebsDevice.VolumeSize = aws.Int64(int64(v.(int))) } diff --git a/aws/resource_aws_launch_template_test.go b/aws/resource_aws_launch_template_test.go index abe1c477cc1..ce71867437a 100644 --- a/aws/resource_aws_launch_template_test.go +++ b/aws/resource_aws_launch_template_test.go @@ -143,6 +143,7 @@ func TestAccAWSLaunchTemplate_BlockDeviceMappings_EBS(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "block_device_mappings.#", "1"), resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.device_name", "/dev/xvda"), resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.ebs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.ebs.0.throughput", "0"), resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.ebs.0.volume_size", "15"), ), }, @@ -196,6 +197,38 @@ func TestAccAWSLaunchTemplate_BlockDeviceMappings_EBS_DeleteOnTermination(t *tes }) } +func TestAccAWSLaunchTemplate_BlockDeviceMappings_EBS_Gp3(t *testing.T) { + var template ec2.LaunchTemplate + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_launch_template.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLaunchTemplateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLaunchTemplateConfig_BlockDeviceMappings_EBS_Gp3(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLaunchTemplateExists(resourceName, &template), + resource.TestCheckResourceAttr(resourceName, "block_device_mappings.#", "1"), + resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.device_name", "/dev/xvda"), + resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.ebs.#", "1"), + resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.ebs.0.iops", "4000"), + resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.ebs.0.throughput", "500"), + resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.ebs.0.volume_size", "15"), + resource.TestCheckResourceAttr(resourceName, "block_device_mappings.0.ebs.0.volume_type", "gp3"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSLaunchTemplate_EbsOptimized(t *testing.T) { var template ec2.LaunchTemplate rName := acctest.RandomWithPrefix("tf-acc-test") @@ -1270,6 +1303,46 @@ resource "aws_autoscaling_group" "test" { `, rName, deleteOnTermination)) } +func testAccAWSLaunchTemplateConfig_BlockDeviceMappings_EBS_Gp3(rName string) string { + return composeConfig( + testAccLatestAmazonLinuxHvmEbsAmiConfig(), + testAccAvailableAZsNoOptInConfig(), + testAccAvailableEc2InstanceTypeForRegion("t3.micro", "t2.micro"), + fmt.Sprintf(` +resource "aws_launch_template" "test" { + image_id = data.aws_ami.amzn-ami-minimal-hvm-ebs.id + instance_type = data.aws_ec2_instance_type_offering.available.instance_type + name = %[1]q + + block_device_mappings { + device_name = "/dev/xvda" + + ebs { + iops = 4000 + throughput = 500 + volume_size = 15 + volume_type = "gp3" + } + } +} + +# Creating an AutoScaling Group verifies the launch template +# ValidationError: You must use a valid fully-formed launch template. the encrypted flag cannot be specified since device /dev/sda1 has a snapshot specified. +resource "aws_autoscaling_group" "test" { + availability_zones = [data.aws_availability_zones.available.names[0]] + desired_capacity = 0 + max_size = 0 + min_size = 0 + name = %[1]q + + launch_template { + id = aws_launch_template.test.id + version = aws_launch_template.test.default_version + } +} +`, rName)) +} + func testAccAWSLaunchTemplateConfig_NetworkInterfaces_DeleteOnTermination(rName string, deleteOnTermination string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { diff --git a/website/docs/r/launch_template.html.markdown b/website/docs/r/launch_template.html.markdown index 6adf5f4c5ac..82a64eb6dd9 100644 --- a/website/docs/r/launch_template.html.markdown +++ b/website/docs/r/launch_template.html.markdown @@ -179,8 +179,9 @@ The `ebs` block supports the following: * `kms_key_id` - The ARN of the AWS Key Management Service (AWS KMS) customer master key (CMK) to use when creating the encrypted volume. `encrypted` must be set to `true` when this is set. * `snapshot_id` - The Snapshot ID to mount. +* `thoughput` - The throughput to provision for a `gp3` volume, with a maximum of 1,000 MiB/s. * `volume_size` - The size of the volume in gigabytes. -* `volume_type` - The type of volume. Can be `"standard"`, `"gp2"`, `"io1"` or `"io2"`. (Default: `"standard"`). +* `volume_type` - The volume type. Can be `standard`, `gp2`, `gp3`, `io1`, `io2`, `sc1` or `st1` (Default: `gp2`). ### Capacity Reservation Specification