diff --git a/builtin/providers/aws/resource_aws_instance.go b/builtin/providers/aws/resource_aws_instance.go index 7681d6ed21e7..892235965c10 100644 --- a/builtin/providers/aws/resource_aws_instance.go +++ b/builtin/providers/aws/resource_aws_instance.go @@ -931,8 +931,16 @@ func readBlockDeviceMappingsFromConfig( ebs.VolumeType = aws.String(v) } - if v, ok := bd["iops"].(int); ok && v > 0 { + if v, ok := bd["iops"].(int); ok && v > 0 && *ebs.VolumeType == "io1" { + // Only set the iops attribute if the volume type is io1. 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 + // See https://github.com/hashicorp/terraform/issues/7765 ebs.Iops = aws.Int64(int64(v)) + } else if v, ok := bd["iops"].(int); ok && v > 0 && *ebs.VolumeType != "io1" { + // Message user about incompatibility + log.Printf("[WARN] IOPs is only valid for storate type io1 for EBS Volumes") } if dn, err := fetchRootDeviceName(d.Get("ami").(string), conn); err == nil { diff --git a/builtin/providers/aws/resource_aws_instance_test.go b/builtin/providers/aws/resource_aws_instance_test.go index 0efcd5fe8b8e..0abc4d6b35eb 100644 --- a/builtin/providers/aws/resource_aws_instance_test.go +++ b/builtin/providers/aws/resource_aws_instance_test.go @@ -105,6 +105,56 @@ func TestAccAWSInstance_basic(t *testing.T) { }) } +func TestAccAWSInstance_GP2IopsDevice(t *testing.T) { + var v ec2.Instance + + testCheck := func() resource.TestCheckFunc { + return func(*terraform.State) error { + + // Map out the block devices by name, which should be unique. + blockDevices := make(map[string]*ec2.InstanceBlockDeviceMapping) + for _, blockDevice := range v.BlockDeviceMappings { + blockDevices[*blockDevice.DeviceName] = blockDevice + } + + // Check if the root block device exists. + if _, ok := blockDevices["/dev/sda1"]; !ok { + return fmt.Errorf("block device doesn't exist: /dev/sda1") + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_instance.foo", + IDRefreshIgnore: []string{ + "ephemeral_block_device", "user_data", "security_groups", "vpc_security_groups"}, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGP2IopsDevice, + //Config: testAccInstanceConfigBlockDevices, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "aws_instance.foo", &v), + resource.TestCheckResourceAttr( + "aws_instance.foo", "root_block_device.#", "1"), + resource.TestCheckResourceAttr( + "aws_instance.foo", "root_block_device.0.volume_size", "11"), + resource.TestCheckResourceAttr( + "aws_instance.foo", "root_block_device.0.volume_type", "gp2"), + resource.TestCheckResourceAttr( + "aws_instance.foo", "root_block_device.0.iops", "100"), + testCheck(), + ), + }, + }, + }) +} + func TestAccAWSInstance_blockDevices(t *testing.T) { var v ec2.Instance @@ -738,6 +788,24 @@ resource "aws_instance" "foo" { } ` +const testAccInstanceGP2IopsDevice = ` +resource "aws_instance" "foo" { + # us-west-2 + ami = "ami-55a7ea65" + + # In order to attach an encrypted volume to an instance you need to have an + # m3.medium or larger. See "Supported Instance Types" in: + # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html + instance_type = "m3.medium" + + root_block_device { + volume_type = "gp2" + volume_size = 11 + iops = 330 + } +} +` + const testAccInstanceConfigBlockDevices = ` resource "aws_instance" "foo" { # us-west-2