diff --git a/aws/resource_aws_ami.go b/aws/resource_aws_ami.go index c6f3925e4954..57a5edaac6aa 100644 --- a/aws/resource_aws_ami.go +++ b/aws/resource_aws_ami.go @@ -119,17 +119,11 @@ func resourceAwsAmi() *schema.Resource { }, "volume_type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: ec2.VolumeTypeStandard, - ValidateFunc: validation.StringInSlice([]string{ - ec2.VolumeTypeStandard, - ec2.VolumeTypeIo1, - ec2.VolumeTypeGp2, - ec2.VolumeTypeSc1, - ec2.VolumeTypeSt1, - }, false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: ec2.VolumeTypeStandard, + ValidateFunc: validation.StringInSlice(ec2.VolumeType_Values(), false), }, }, }, diff --git a/aws/resource_aws_instance.go b/aws/resource_aws_instance.go index 47b11d7d0470..125571feeb3a 100644 --- a/aws/resource_aws_instance.go +++ b/aws/resource_aws_instance.go @@ -392,17 +392,11 @@ func resourceAwsInstance() *schema.Resource { }, "volume_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - ec2.VolumeTypeStandard, - ec2.VolumeTypeIo1, - ec2.VolumeTypeGp2, - ec2.VolumeTypeSc1, - ec2.VolumeTypeSt1, - }, false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(ec2.VolumeType_Values(), false), }, "volume_id": { @@ -504,16 +498,10 @@ func resourceAwsInstance() *schema.Resource { }, "volume_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice([]string{ - ec2.VolumeTypeStandard, - ec2.VolumeTypeIo1, - ec2.VolumeTypeGp2, - ec2.VolumeTypeSc1, - ec2.VolumeTypeSt1, - }, false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(ec2.VolumeType_Values(), false), }, "volume_id": { @@ -592,11 +580,11 @@ func resourceAwsInstance() *schema.Resource { } func iopsDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool { - // Suppress diff if volume_type is not io1 and iops is unset or configured as 0 + // Suppress diff if volume_type is not io1 or io2 and iops is unset or configured as 0 i := strings.LastIndexByte(k, '.') vt := k[:i+1] + "volume_type" v := d.Get(vt).(string) - return strings.ToLower(v) != ec2.VolumeTypeIo1 && new == "0" + return (strings.ToLower(v) != ec2.VolumeTypeIo1 || strings.ToLower(v) != ec2.VolumeTypeIo2) && new == "0" } func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { @@ -1369,7 +1357,7 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error { if v, ok := d.Get("root_block_device.0.iops").(int); ok && v != 0 { // Enforce IOPs usage with a valid volume type // Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/12667 - if t, ok := d.Get("root_block_device.0.volume_type").(string); ok && t != ec2.VolumeTypeIo1 { + if t, ok := d.Get("root_block_device.0.volume_type").(string); ok && t != ec2.VolumeTypeIo1 && t != ec2.VolumeTypeIo2 { if t == "" { // Volume defaults to gp2 t = ec2.VolumeTypeGp2 @@ -1833,8 +1821,8 @@ func readBlockDeviceMappingsFromConfig(d *schema.ResourceData, conn *ec2.EC2) ([ if v, ok := bd["volume_type"].(string); ok && v != "" { ebs.VolumeType = aws.String(v) if iops, ok := bd["iops"].(int); ok && iops > 0 { - if ec2.VolumeTypeIo1 == strings.ToLower(v) { - // Condition: This parameter is required for requests to create io1 + if ec2.VolumeTypeIo1 == strings.ToLower(v) || ec2.VolumeTypeIo2 == strings.ToLower(v) { + // Condition: This parameter is required for requests to create io1 or io2 // volumes; it is not used in requests to create gp2, st1, sc1, or // standard volumes. // See: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html @@ -1899,8 +1887,8 @@ func readBlockDeviceMappingsFromConfig(d *schema.ResourceData, conn *ec2.EC2) ([ if v, ok := bd["volume_type"].(string); ok && v != "" { ebs.VolumeType = aws.String(v) if iops, ok := bd["iops"].(int); ok && iops > 0 { - if ec2.VolumeTypeIo1 == strings.ToLower(v) { - // Only set the iops attribute if the volume type is io1. Setting otherwise + if ec2.VolumeTypeIo1 == strings.ToLower(v) || ec2.VolumeTypeIo2 == strings.ToLower(v) { + // Only set the iops attribute if the volume type is io1 or io2. Setting otherwise // can trigger a refresh/plan loop based on the computed value that is given // from AWS, and prevent us from specifying 0 as a valid iops. // See https://github.com/hashicorp/terraform/pull/4146 diff --git a/aws/resource_aws_instance_test.go b/aws/resource_aws_instance_test.go index 51de738c1242..e25ba252f355 100644 --- a/aws/resource_aws_instance_test.go +++ b/aws/resource_aws_instance_test.go @@ -1600,7 +1600,7 @@ func TestAccAWSInstance_EbsRootDevice_ModifyType(t *testing.T) { }) } -func TestAccAWSInstance_EbsRootDevice_ModifyIOPS(t *testing.T) { +func TestAccAWSInstance_EbsRootDevice_ModifyIOPS_Io1(t *testing.T) { var original ec2.Instance var updated ec2.Instance resourceName := "aws_instance.test" @@ -1642,6 +1642,48 @@ func TestAccAWSInstance_EbsRootDevice_ModifyIOPS(t *testing.T) { }) } +func TestAccAWSInstance_EbsRootDevice_ModifyIOPS_Io2(t *testing.T) { + var original ec2.Instance + var updated ec2.Instance + resourceName := "aws_instance.test" + + volumeSize := "30" + deleteOnTermination := "true" + volumeType := "io2" + + originalIOPS := "100" + updatedIOPS := "200" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEc2InstanceRootBlockDeviceWithIOPS(volumeSize, deleteOnTermination, volumeType, originalIOPS), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &original), + resource.TestCheckResourceAttr(resourceName, "root_block_device.0.volume_size", volumeSize), + resource.TestCheckResourceAttr(resourceName, "root_block_device.0.delete_on_termination", deleteOnTermination), + resource.TestCheckResourceAttr(resourceName, "root_block_device.0.volume_type", volumeType), + resource.TestCheckResourceAttr(resourceName, "root_block_device.0.iops", originalIOPS), + ), + }, + { + Config: testAccAwsEc2InstanceRootBlockDeviceWithIOPS(volumeSize, deleteOnTermination, volumeType, updatedIOPS), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &updated), + testAccCheckInstanceNotRecreated(t, &original, &updated), + resource.TestCheckResourceAttr(resourceName, "root_block_device.0.volume_size", volumeSize), + resource.TestCheckResourceAttr(resourceName, "root_block_device.0.delete_on_termination", deleteOnTermination), + resource.TestCheckResourceAttr(resourceName, "root_block_device.0.volume_type", volumeType), + resource.TestCheckResourceAttr(resourceName, "root_block_device.0.iops", updatedIOPS), + ), + }, + }, + }) +} + func TestAccAWSInstance_EbsRootDevice_ModifyDeleteOnTermination(t *testing.T) { var original ec2.Instance var updated ec2.Instance diff --git a/aws/resource_aws_launch_template.go b/aws/resource_aws_launch_template.go index b47b9b74c99b..b217ac380e43 100644 --- a/aws/resource_aws_launch_template.go +++ b/aws/resource_aws_launch_template.go @@ -139,16 +139,10 @@ func resourceAwsLaunchTemplate() *schema.Resource { Computed: true, }, "volume_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice([]string{ - ec2.VolumeTypeStandard, - ec2.VolumeTypeGp2, - ec2.VolumeTypeIo1, - ec2.VolumeTypeSc1, - ec2.VolumeTypeSt1, - }, false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(ec2.VolumeType_Values(), false), }, }, }, diff --git a/aws/resource_aws_spot_fleet_request.go b/aws/resource_aws_spot_fleet_request.go index e7fe6fb0e4f5..073863f4d8b5 100644 --- a/aws/resource_aws_spot_fleet_request.go +++ b/aws/resource_aws_spot_fleet_request.go @@ -125,17 +125,11 @@ func resourceAwsSpotFleetRequest() *schema.Resource { ForceNew: true, }, "volume_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - ec2.VolumeTypeStandard, - ec2.VolumeTypeIo1, - ec2.VolumeTypeGp2, - ec2.VolumeTypeSc1, - ec2.VolumeTypeSt1, - }, false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(ec2.VolumeType_Values(), false), }, }, }, @@ -204,17 +198,11 @@ func resourceAwsSpotFleetRequest() *schema.Resource { ForceNew: true, }, "volume_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - ec2.VolumeTypeStandard, - ec2.VolumeTypeIo1, - ec2.VolumeTypeGp2, - ec2.VolumeTypeSc1, - ec2.VolumeTypeSt1, - }, false), + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(ec2.VolumeType_Values(), false), }, }, }, diff --git a/website/docs/r/ami.html.markdown b/website/docs/r/ami.html.markdown index 3eacdd30a62b..f013c5f4d567 100644 --- a/website/docs/r/ami.html.markdown +++ b/website/docs/r/ami.html.markdown @@ -74,7 +74,7 @@ Nested `ebs_block_device` blocks have the following structure: * `delete_on_termination` - (Optional) Boolean controlling whether the EBS volumes created to support each created instance will be deleted once that instance is terminated. * `encrypted` - (Optional) Boolean controlling whether the created EBS volumes will be encrypted. Can't be used with `snapshot_id`. -* `iops` - (Required only when `volume_type` is "io1") Number of I/O operations per second the +* `iops` - (Required only when `volume_type` is "io1/io2") Number of I/O operations per second the created volumes will support. * `snapshot_id` - (Optional) The id of an EBS snapshot that will be used to initialize the created EBS volumes. If set, the `volume_size` attribute must be at least as large as the referenced @@ -83,7 +83,7 @@ Nested `ebs_block_device` blocks have the following structure: If `snapshot_id` is set and `volume_size` is omitted then the volume will have the same size as the selected snapshot. * `volume_type` - (Optional) The type of EBS volume to create. Can be one of "standard" (the - default), "io1" or "gp2". + default), "io1", "io2" or "gp2". * `kms_key_id` - (Optional) The full ARN of the AWS Key Management Service (AWS KMS) CMK to use when encrypting the snapshots of an image during a copy operation. This parameter is only required if you want to use a non-default CMK; if this parameter is not specified, the default CMK for EBS is used diff --git a/website/docs/r/ebs_volume.html.markdown b/website/docs/r/ebs_volume.html.markdown index 9e5251f9191f..4bf9dced640d 100644 --- a/website/docs/r/ebs_volume.html.markdown +++ b/website/docs/r/ebs_volume.html.markdown @@ -31,7 +31,7 @@ The following arguments are supported: * `availability_zone` - (Required) The AZ where the EBS volume will exist. * `encrypted` - (Optional) If true, the disk will be encrypted. -* `iops` - (Optional) The amount of IOPS to provision for the disk. Only valid for `type` of `io1`. +* `iops` - (Optional) The amount of IOPS to provision for the disk. Only valid for `type` of `io1` or `io2`. * `multi_attach_enabled` - (Optional) Specifies whether to enable Amazon EBS Multi-Attach. Multi-Attach is supported exclusively on `io1` volumes. * `size` - (Optional) The size of the drive in GiBs. * `snapshot_id` (Optional) A snapshot to base the EBS volume off of. diff --git a/website/docs/r/instance.html.markdown b/website/docs/r/instance.html.markdown index fef4b91b068e..55d6a9498df9 100644 --- a/website/docs/r/instance.html.markdown +++ b/website/docs/r/instance.html.markdown @@ -126,11 +126,11 @@ to understand the implications of using these attributes. The `root_block_device` mapping supports the following: -* `volume_type` - (Optional) The type of volume. Can be `"standard"`, `"gp2"`, `"io1"`, `"sc1"`, or `"st1"`. (Default: `"gp2"`). +* `volume_type` - (Optional) The type of volume. Can be `"standard"`, `"gp2"`, `"io1"`, `"io2"`, `"sc1"`, or `"st1"`. (Default: `"gp2"`). * `volume_size` - (Optional) The size of the volume in gibibytes (GiB). * `iops` - (Optional) The amount of provisioned [IOPS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-io-characteristics.html). - This is only valid for `volume_type` of `"io1"`, and must be specified if + This is only valid for `volume_type` of `"io1/io2"`, and must be specified if using that type * `delete_on_termination` - (Optional) Whether the volume should be destroyed on instance termination (Default: `true`). @@ -144,12 +144,12 @@ Each `ebs_block_device` supports the following: * `device_name` - (Required) The name of the device to mount. * `snapshot_id` - (Optional) The Snapshot ID to mount. -* `volume_type` - (Optional) The type of volume. Can be `"standard"`, `"gp2"`, - or `"io1"`. (Default: `"gp2"`). +* `volume_type` - (Optional) The type of volume. Can be `"standard"`, `"gp2"`, `"io1"` + or `"io2"`. (Default: `"gp2"`). * `volume_size` - (Optional) The size of the volume in gibibytes (GiB). * `iops` - (Optional) The amount of provisioned [IOPS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-io-characteristics.html). - This must be set with a `volume_type` of `"io1"`. + This must be set with a `volume_type` of `"io1/io2"`. * `delete_on_termination` - (Optional) Whether the volume should be destroyed on instance termination (Default: `true`). * `encrypted` - (Optional) Enables [EBS diff --git a/website/docs/r/launch_template.html.markdown b/website/docs/r/launch_template.html.markdown index 69524c263ec9..170fd545077f 100644 --- a/website/docs/r/launch_template.html.markdown +++ b/website/docs/r/launch_template.html.markdown @@ -175,12 +175,12 @@ The `ebs` block supports the following: on the volume (Default: `false`). Cannot be used with `snapshot_id`. * `iops` - The amount of provisioned [IOPS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-io-characteristics.html). - This must be set with a `volume_type` of `"io1"`. + This must be set with a `volume_type` of `"io1/io2"`. * `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. * `volume_size` - The size of the volume in gigabytes. -* `volume_type` - The type of volume. Can be `"standard"`, `"gp2"`, or `"io1"`. (Default: `"standard"`). +* `volume_type` - The type of volume. Can be `"standard"`, `"gp2"`, `"io1"` or `"io2"`. (Default: `"standard"`). ### Capacity Reservation Specification