diff --git a/aws/data_source_aws_instance.go b/aws/data_source_aws_instance.go index db576e5305a..08618d30526 100644 --- a/aws/data_source_aws_instance.go +++ b/aws/data_source_aws_instance.go @@ -87,6 +87,11 @@ func dataSourceAwsInstance() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "secondary_private_ips": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "iam_instance_profile": { Type: schema.TypeString, Computed: true, @@ -436,6 +441,16 @@ func instanceDescriptionAttributes(d *schema.ResourceData, instance *ec2.Instanc d.Set("subnet_id", ni.SubnetId) d.Set("network_interface_id", ni.NetworkInterfaceId) d.Set("associate_public_ip_address", ni.Association != nil) + + secondaryIPs := make([]string, 0, len(ni.PrivateIpAddresses)) + for _, ip := range ni.PrivateIpAddresses { + if !aws.BoolValue(ip.Primary) { + secondaryIPs = append(secondaryIPs, aws.StringValue(ip.PrivateIpAddress)) + } + } + if err := d.Set("secondary_private_ips", secondaryIPs); err != nil { + return fmt.Errorf("error setting secondary_private_ips: %w", err) + } } } } else { diff --git a/aws/data_source_aws_instance_test.go b/aws/data_source_aws_instance_test.go index faf187f9b95..06f35c5e86a 100644 --- a/aws/data_source_aws_instance_test.go +++ b/aws/data_source_aws_instance_test.go @@ -193,6 +193,27 @@ func TestAccAWSInstanceDataSource_privateIP(t *testing.T) { }) } +func TestAccAWSInstanceDataSource_secondaryPrivateIPs(t *testing.T) { + resourceName := "aws_instance.test" + datasourceName := "data.aws_instance.test" + rName := fmt.Sprintf("tf-testacc-instance-%s", acctest.RandStringFromCharSet(12, acctest.CharSetAlphaNum)) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccInstanceDataSourceConfig_secondaryPrivateIPs(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "ami", resourceName, "ami"), + resource.TestCheckResourceAttrPair(datasourceName, "instance_type", resourceName, "instance_type"), + resource.TestCheckResourceAttrPair(datasourceName, "secondary_private_ips", resourceName, "secondary_private_ips"), + ), + }, + }, + }) +} + func TestAccAWSInstanceDataSource_keyPair(t *testing.T) { resourceName := "aws_instance.test" datasourceName := "data.aws_instance.test" @@ -665,6 +686,21 @@ data "aws_instance" "test" { `) } +func testAccInstanceDataSourceConfig_secondaryPrivateIPs(rName string) string { + return testAccLatestAmazonLinuxHvmEbsAmiConfig() + testAccAwsInstanceVpcConfig(rName, false) + fmt.Sprintf(` +resource "aws_instance" "test" { + ami = "${data.aws_ami.amzn-ami-minimal-hvm-ebs.id}" + instance_type = "t2.micro" + subnet_id = "${aws_subnet.test.id}" + secondary_private_ips = ["10.1.1.42"] +} + +data "aws_instance" "test" { + instance_id = "${aws_instance.test.id}" +} +`) +} + func testAccInstanceDataSourceConfig_keyPair(rName string) string { return testAccLatestAmazonLinuxHvmEbsAmiConfig() + fmt.Sprintf(` resource "aws_key_pair" "test" { diff --git a/aws/resource_aws_instance.go b/aws/resource_aws_instance.go index abb2739a4f1..28619a09776 100644 --- a/aws/resource_aws_instance.go +++ b/aws/resource_aws_instance.go @@ -115,6 +115,16 @@ func resourceAwsInstance() *schema.Resource { ), }, + "secondary_private_ips": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.IsIPv4Address, + }, + }, + "source_dest_check": { Type: schema.TypeBool, Optional: true, @@ -206,7 +216,7 @@ func resourceAwsInstance() *schema.Resource { }, "network_interface": { - ConflictsWith: []string{"associate_public_ip_address", "subnet_id", "private_ip", "vpc_security_group_ids", "security_groups", "ipv6_addresses", "ipv6_address_count", "source_dest_check"}, + ConflictsWith: []string{"associate_public_ip_address", "subnet_id", "private_ip", "secondary_private_ips", "vpc_security_group_ids", "security_groups", "ipv6_addresses", "ipv6_address_count", "source_dest_check"}, Type: schema.TypeSet, Optional: true, Computed: true, @@ -807,6 +817,7 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { } } + var secondaryPrivateIPs []string var ipv6Addresses []string if len(instance.NetworkInterfaces) > 0 { var primaryNetworkInterface ec2.InstanceNetworkInterface @@ -851,6 +862,12 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { d.Set("associate_public_ip_address", primaryNetworkInterface.Association != nil) + for _, address := range primaryNetworkInterface.PrivateIpAddresses { + if !aws.BoolValue(address.Primary) { + secondaryPrivateIPs = append(secondaryPrivateIPs, aws.StringValue(address.PrivateIpAddress)) + } + } + for _, address := range primaryNetworkInterface.Ipv6Addresses { ipv6Addresses = append(ipv6Addresses, aws.StringValue(address.Ipv6Address)) } @@ -862,6 +879,10 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { d.Set("subnet_id", instance.SubnetId) } + if err := d.Set("secondary_private_ips", secondaryPrivateIPs); err != nil { + return fmt.Errorf("Error setting private_ips for AWS Instance (%s): %w", d.Id(), err) + } + if err := d.Set("ipv6_addresses", ipv6Addresses); err != nil { log.Printf("[WARN] Error setting ipv6_addresses for AWS Instance (%s): %s", d.Id(), err) } @@ -1109,23 +1130,7 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error { } } - if d.HasChange("vpc_security_group_ids") && !d.IsNewResource() { - var groups []*string - if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 { - for _, v := range v.List() { - groups = append(groups, aws.String(v.(string))) - } - } - - if len(groups) < 1 { - return fmt.Errorf("VPC-based instances require at least one security group to be attached.") - } - // If a user has multiple network interface attachments on the target EC2 instance, simply modifying the - // instance attributes via a `ModifyInstanceAttributes()` request would fail with the following error message: - // "There are multiple interfaces attached to instance 'i-XX'. Please specify an interface ID for the operation instead." - // Thus, we need to actually modify the primary network interface for the new security groups, as the primary - // network interface is where we modify/create security group assignments during Create. - log.Printf("[INFO] Modifying `vpc_security_group_ids` on Instance %q", d.Id()) + if d.HasChanges("secondary_private_ips", "vpc_security_group_ids") && !d.IsNewResource() { instance, err := resourceAwsInstanceFindByID(conn, d.Id()) if err != nil { return fmt.Errorf("error retrieving instance %q: %w", d.Id(), err) @@ -1137,18 +1142,78 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error { } } - if primaryInterface.NetworkInterfaceId == nil { - log.Print("[Error] Attempted to set vpc_security_group_ids on an instance without a primary network interface") - return fmt.Errorf( - "Failed to update vpc_security_group_ids on %q, which does not contain a primary network interface", - d.Id()) + if d.HasChange("secondary_private_ips") { + if primaryInterface.NetworkInterfaceId == nil { + return fmt.Errorf("Failed to update secondary_private_ips on %q, which does not contain a primary network interface", + d.Id()) + } + o, n := d.GetChange("secondary_private_ips") + if o == nil { + o = new(schema.Set) + } + if n == nil { + n = new(schema.Set) + } + + os := o.(*schema.Set) + ns := n.(*schema.Set) + + // Unassign old IP addresses + unassignIps := os.Difference(ns) + if unassignIps.Len() != 0 { + input := &ec2.UnassignPrivateIpAddressesInput{ + NetworkInterfaceId: primaryInterface.NetworkInterfaceId, + PrivateIpAddresses: expandStringSet(unassignIps), + } + log.Printf("[INFO] Unassigning secondary_private_ips on Instance %q", d.Id()) + _, err := conn.UnassignPrivateIpAddresses(input) + if err != nil { + return fmt.Errorf("Failure to unassign Secondary Private IPs: %w", err) + } + } + + // Assign new IP addresses + assignIps := ns.Difference(os) + if assignIps.Len() != 0 { + input := &ec2.AssignPrivateIpAddressesInput{ + NetworkInterfaceId: primaryInterface.NetworkInterfaceId, + PrivateIpAddresses: expandStringSet(assignIps), + } + log.Printf("[INFO] Assigning secondary_private_ips on Instance %q", d.Id()) + _, err := conn.AssignPrivateIpAddresses(input) + if err != nil { + return fmt.Errorf("Failure to assign Secondary Private IPs: %w", err) + } + } } - if _, err := conn.ModifyNetworkInterfaceAttribute(&ec2.ModifyNetworkInterfaceAttributeInput{ - NetworkInterfaceId: primaryInterface.NetworkInterfaceId, - Groups: groups, - }); err != nil { - return err + if d.HasChange("vpc_security_group_ids") { + if primaryInterface.NetworkInterfaceId == nil { + return fmt.Errorf("Failed to update vpc_security_group_ids on %q, which does not contain a primary network interface", + d.Id()) + } + var groups []*string + if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 { + for _, v := range v.List() { + groups = append(groups, aws.String(v.(string))) + } + } + + if len(groups) < 1 { + return fmt.Errorf("VPC-based instances require at least one security group to be attached.") + } + // If a user has multiple network interface attachments on the target EC2 instance, simply modifying the + // instance attributes via a `ModifyInstanceAttributes()` request would fail with the following error message: + // "There are multiple interfaces attached to instance 'i-XX'. Please specify an interface ID for the operation instead." + // Thus, we need to actually modify the primary network interface for the new security groups, as the primary + // network interface is where we modify/create security group assignments during Create. + log.Printf("[INFO] Modifying `vpc_security_group_ids` on Instance %q", d.Id()) + if _, err := conn.ModifyNetworkInterfaceAttribute(&ec2.ModifyNetworkInterfaceAttributeInput{ + NetworkInterfaceId: primaryInterface.NetworkInterfaceId, + Groups: groups, + }); err != nil { + return err + } } } @@ -1687,6 +1752,10 @@ func buildNetworkInterfaceOpts(d *schema.ResourceData, groups []*string, nInterf ni.PrivateIpAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("secondary_private_ips"); ok && v.(*schema.Set).Len() > 0 { + ni.PrivateIpAddresses = expandSecondaryPrivateIPAddresses(v.(*schema.Set).List()) + } + if v, ok := d.GetOk("ipv6_address_count"); ok { ni.Ipv6AddressCount = aws.Int64(int64(v.(int))) } @@ -2312,6 +2381,19 @@ func expandEc2InstanceMetadataOptions(l []interface{}) *ec2.InstanceMetadataOpti return opts } +//Expands an array of secondary Private IPs into a ec2 Private IP Address Spec +func expandSecondaryPrivateIPAddresses(ips []interface{}) []*ec2.PrivateIpAddressSpecification { + specs := make([]*ec2.PrivateIpAddressSpecification, 0, len(ips)) + for _, v := range ips { + spec := &ec2.PrivateIpAddressSpecification{ + PrivateIpAddress: aws.String(v.(string)), + } + + specs = append(specs, spec) + } + return specs +} + func flattenEc2InstanceMetadataOptions(opts *ec2.InstanceMetadataOptionsResponse) []interface{} { if opts == nil { return nil diff --git a/aws/resource_aws_instance_test.go b/aws/resource_aws_instance_test.go index 74bae1825e0..73905a954e8 100644 --- a/aws/resource_aws_instance_test.go +++ b/aws/resource_aws_instance_test.go @@ -2014,6 +2014,196 @@ func TestAccAWSInstance_addSecurityGroupNetworkInterface(t *testing.T) { }) } +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/7063 +func TestAccAWSInstance_NewNetworkInterface_PublicIPAndSecondaryPrivateIPs(t *testing.T) { + var v ec2.Instance + resourceName := "aws_instance.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInstanceConfigPublicAndPrivateSecondaryIPs(rName, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "associate_public_ip_address", "true"), + resource.TestCheckResourceAttrSet(resourceName, "public_ip"), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "2"), + ), + }, + { + Config: testAccInstanceConfigPublicAndPrivateSecondaryIPs(rName, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "associate_public_ip_address", "false"), + resource.TestCheckResourceAttr(resourceName, "public_ip", ""), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/7063 +func TestAccAWSInstance_NewNetworkInterface_EmptyPrivateIPAndSecondaryPrivateIPs(t *testing.T) { + var v ec2.Instance + resourceName := "aws_instance.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + secondaryIPs := fmt.Sprintf("%q, %q", "10.1.1.42", "10.1.1.43") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInstanceConfigPrivateIPAndSecondaryIPs(rName, "", secondaryIPs), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttrSet(resourceName, "private_ip"), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/7063 +func TestAccAWSInstance_NewNetworkInterface_EmptyPrivateIPAndSecondaryPrivateIPsUpdate(t *testing.T) { + var v ec2.Instance + resourceName := "aws_instance.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + secondaryIP := fmt.Sprintf("%q", "10.1.1.42") + secondaryIPs := fmt.Sprintf("%s, %q", secondaryIP, "10.1.1.43") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInstanceConfigPrivateIPAndSecondaryIPs(rName, "", secondaryIPs), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttrSet(resourceName, "private_ip"), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "2"), + ), + }, + { + Config: testAccInstanceConfigPrivateIPAndSecondaryIPs(rName, "", ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttrSet(resourceName, "private_ip"), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "0"), + ), + }, + { + Config: testAccInstanceConfigPrivateIPAndSecondaryIPs(rName, "", secondaryIP), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttrSet(resourceName, "private_ip"), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/7063 +func TestAccAWSInstance_NewNetworkInterface_PrivateIPAndSecondaryPrivateIPs(t *testing.T) { + var v ec2.Instance + resourceName := "aws_instance.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + privateIP := "10.1.1.42" + secondaryIPs := fmt.Sprintf("%q, %q", "10.1.1.43", "10.1.1.44") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInstanceConfigPrivateIPAndSecondaryIPs(rName, privateIP, secondaryIPs), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "private_ip", privateIP), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/7063 +func TestAccAWSInstance_NewNetworkInterface_PrivateIPAndSecondaryPrivateIPsUpdate(t *testing.T) { + var v ec2.Instance + resourceName := "aws_instance.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + privateIP := "10.1.1.42" + secondaryIP := fmt.Sprintf("%q", "10.1.1.43") + secondaryIPs := fmt.Sprintf("%s, %q", secondaryIP, "10.1.1.44") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInstanceConfigPrivateIPAndSecondaryIPs(rName, privateIP, secondaryIPs), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "private_ip", privateIP), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "2"), + ), + }, + { + Config: testAccInstanceConfigPrivateIPAndSecondaryIPs(rName, privateIP, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttrSet(resourceName, "private_ip"), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "0"), + ), + }, + { + Config: testAccInstanceConfigPrivateIPAndSecondaryIPs(rName, privateIP, secondaryIP), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "private_ip", privateIP), + resource.TestCheckResourceAttr(resourceName, "secondary_private_ips.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + // https://github.com/terraform-providers/terraform-provider-aws/issues/227 func TestAccAWSInstance_associatePublic_defaultPrivate(t *testing.T) { var v ec2.Instance @@ -4280,6 +4470,61 @@ resource "aws_network_interface" "test" { `, rName) } +func testAccInstanceConfigPublicAndPrivateSecondaryIPs(rName string, isPublic bool) string { + return testAccLatestAmazonLinuxHvmEbsAmiConfig() + testAccAwsInstanceVpcConfig(rName, false) + fmt.Sprintf(` +resource "aws_security_group" "test" { + vpc_id = "${aws_vpc.test.id}" + description = "%[1]s" + name = "%[1]s" +} + +resource "aws_instance" "test" { + ami = "${data.aws_ami.amzn-ami-minimal-hvm-ebs.id}" + instance_type = "t3.small" + subnet_id = "${aws_subnet.test.id}" + + associate_public_ip_address = %[2]t + + secondary_private_ips = ["10.1.1.42", "10.1.1.43"] + + vpc_security_group_ids = [ + "${aws_security_group.test.id}" + ] + + tags = { + Name = %[1]q + } +} +`, rName, isPublic) +} + +func testAccInstanceConfigPrivateIPAndSecondaryIPs(rName, privateIP, secondaryIPs string) string { + return testAccLatestAmazonLinuxHvmEbsAmiConfig() + testAccAwsInstanceVpcConfig(rName, false) + fmt.Sprintf(` +resource "aws_security_group" "test" { + vpc_id = "${aws_vpc.test.id}" + description = "%[1]s" + name = "%[1]s" +} + +resource "aws_instance" "test" { + ami = "${data.aws_ami.amzn-ami-minimal-hvm-ebs.id}" + instance_type = "t3.small" + subnet_id = "${aws_subnet.test.id}" + + private_ip = "%[2]s" + secondary_private_ips = [%[3]s] + + vpc_security_group_ids = [ + "${aws_security_group.test.id}" + ] + + tags = { + Name = %[1]q + } +} +`, rName, privateIP, secondaryIPs) +} + func testAccInstanceConfig_associatePublic_defaultPrivate(rName string) string { return testAccLatestAmazonLinuxHvmEbsAmiConfig() + testAccAwsInstanceVpcConfig(rName, false) + fmt.Sprintf(` resource "aws_instance" "test" { diff --git a/website/docs/d/instance.html.markdown b/website/docs/d/instance.html.markdown index f0166770311..83dcb7f3880 100644 --- a/website/docs/d/instance.html.markdown +++ b/website/docs/d/instance.html.markdown @@ -92,6 +92,7 @@ interpolation. used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC. * `private_ip` - The private IP address assigned to the Instance. +* `secondary_private_ips` - The secondary private IPv4 addresses assigned to the instance's primary network interface (eth0) in a VPC. * `public_dns` - The public DNS name assigned to the Instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC. * `public_ip` - The public IP address assigned to the Instance, if applicable. **NOTE**: If you are using an [`aws_eip`](/docs/providers/aws/r/eip.html) with your instance, you should refer to the EIP's address directly and not use `public_ip`, as this field will change after the EIP is attached. diff --git a/website/docs/r/instance.html.markdown b/website/docs/r/instance.html.markdown index e8bad8872e3..f0bc6810639 100644 --- a/website/docs/r/instance.html.markdown +++ b/website/docs/r/instance.html.markdown @@ -87,6 +87,7 @@ instances. See [Shutdown Behavior](https://docs.aws.amazon.com/AWSEC2/latest/Use * `associate_public_ip_address` - (Optional) Associate a public ip address with an instance in a VPC. Boolean value. * `private_ip` - (Optional) Private IP address to associate with the instance in a VPC. +* `secondary_private_ips` - (Optional) A list of secondary private IPv4 addresses to assign to the instance's primary network interface (eth0) in a VPC. Can only be assigned to the primary network interface (eth0) attached at instance creation, not a pre-existing network interface i.e. referenced in a `network_interface` block. Refer to the [Elastic network interfaces documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html#AvailableIpPerENI) to see the maximum number of private IP addresses allowed per instance type. * `source_dest_check` - (Optional) Controls if traffic is routed to the instance when the destination address does not match the instance. Used for NAT or VPNs. Defaults true. * `user_data` - (Optional) The user data to provide when launching the instance. Do not pass gzip-compressed data via this argument; see `user_data_base64` instead.