Merge pull request #12933 from hashicorp/f-network-interfaces
provider/aws: Add network_interface to aws_instance
This commit is contained in:
commit
64134418a5
|
@ -128,11 +128,45 @@ func resourceAwsInstance() *schema.Resource {
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// TODO: Deprecate me v0.10.0
|
||||||
"network_interface_id": {
|
"network_interface_id": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
Deprecated: "Please use `primary_network_interface_id` instead",
|
||||||
|
},
|
||||||
|
|
||||||
|
"primary_network_interface_id": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"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"},
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"delete_on_termination": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Default: false,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"network_interface_id": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"device_index": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"public_ip": {
|
"public_ip": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
@ -558,23 +592,62 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
d.Set("private_ip", instance.PrivateIpAddress)
|
d.Set("private_ip", instance.PrivateIpAddress)
|
||||||
d.Set("iam_instance_profile", iamInstanceProfileArnToName(instance.IamInstanceProfile))
|
d.Set("iam_instance_profile", iamInstanceProfileArnToName(instance.IamInstanceProfile))
|
||||||
|
|
||||||
|
// Set configured Network Interface Device Index Slice
|
||||||
|
// We only want to read, and populate state for the configured network_interface attachments. Otherwise, other
|
||||||
|
// resources have the potential to attach network interfaces to the instance, and cause a perpetual create/destroy
|
||||||
|
// diff. We should only read on changes configured for this specific resource because of this.
|
||||||
|
var configuredDeviceIndexes []int
|
||||||
|
if v, ok := d.GetOk("network_interface"); ok {
|
||||||
|
vL := v.(*schema.Set).List()
|
||||||
|
for _, vi := range vL {
|
||||||
|
mVi := vi.(map[string]interface{})
|
||||||
|
configuredDeviceIndexes = append(configuredDeviceIndexes, mVi["device_index"].(int))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var ipv6Addresses []string
|
var ipv6Addresses []string
|
||||||
if len(instance.NetworkInterfaces) > 0 {
|
if len(instance.NetworkInterfaces) > 0 {
|
||||||
for _, ni := range instance.NetworkInterfaces {
|
var primaryNetworkInterface ec2.InstanceNetworkInterface
|
||||||
if *ni.Attachment.DeviceIndex == 0 {
|
var networkInterfaces []map[string]interface{}
|
||||||
d.Set("subnet_id", ni.SubnetId)
|
for _, iNi := range instance.NetworkInterfaces {
|
||||||
d.Set("network_interface_id", ni.NetworkInterfaceId)
|
ni := make(map[string]interface{})
|
||||||
d.Set("associate_public_ip_address", ni.Association != nil)
|
if *iNi.Attachment.DeviceIndex == 0 {
|
||||||
d.Set("ipv6_address_count", len(ni.Ipv6Addresses))
|
primaryNetworkInterface = *iNi
|
||||||
|
}
|
||||||
for _, address := range ni.Ipv6Addresses {
|
// If the attached network device is inside our configuration, refresh state with values found.
|
||||||
ipv6Addresses = append(ipv6Addresses, *address.Ipv6Address)
|
// Otherwise, assume the network device was attached via an outside resource.
|
||||||
|
for _, index := range configuredDeviceIndexes {
|
||||||
|
if index == int(*iNi.Attachment.DeviceIndex) {
|
||||||
|
ni["device_index"] = *iNi.Attachment.DeviceIndex
|
||||||
|
ni["network_interface_id"] = *iNi.NetworkInterfaceId
|
||||||
|
ni["delete_on_termination"] = *iNi.Attachment.DeleteOnTermination
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Don't add empty network interfaces to schema
|
||||||
|
if len(ni) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
networkInterfaces = append(networkInterfaces, ni)
|
||||||
}
|
}
|
||||||
|
if err := d.Set("network_interface", networkInterfaces); err != nil {
|
||||||
|
return fmt.Errorf("Error setting network_interfaces: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set primary network interface details
|
||||||
|
d.Set("subnet_id", primaryNetworkInterface.SubnetId)
|
||||||
|
d.Set("network_interface_id", primaryNetworkInterface.NetworkInterfaceId) // TODO: Deprecate me v0.10.0
|
||||||
|
d.Set("primary_network_interface_id", primaryNetworkInterface.NetworkInterfaceId)
|
||||||
|
d.Set("associate_public_ip_address", primaryNetworkInterface.Association != nil)
|
||||||
|
d.Set("ipv6_address_count", len(primaryNetworkInterface.Ipv6Addresses))
|
||||||
|
|
||||||
|
for _, address := range primaryNetworkInterface.Ipv6Addresses {
|
||||||
|
ipv6Addresses = append(ipv6Addresses, *address.Ipv6Address)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
d.Set("subnet_id", instance.SubnetId)
|
d.Set("subnet_id", instance.SubnetId)
|
||||||
d.Set("network_interface_id", "")
|
d.Set("network_interface_id", "") // TODO: Deprecate me v0.10.0
|
||||||
|
d.Set("primary_network_interface_id", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.Set("ipv6_addresses", ipv6Addresses); err != nil {
|
if err := d.Set("ipv6_addresses", ipv6Addresses); err != nil {
|
||||||
|
@ -716,24 +789,28 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("source_dest_check") || d.IsNewResource() {
|
// SourceDestCheck can only be modified on an instance without manually specified network interfaces.
|
||||||
// SourceDestCheck can only be set on VPC instances // AWS will return an error of InvalidParameterCombination if we attempt
|
// SourceDestCheck, in that case, is configured at the network interface level
|
||||||
// to modify the source_dest_check of an instance in EC2 Classic
|
if _, ok := d.GetOk("network_interface"); !ok {
|
||||||
log.Printf("[INFO] Modifying `source_dest_check` on Instance %s", d.Id())
|
if d.HasChange("source_dest_check") || d.IsNewResource() {
|
||||||
_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
// SourceDestCheck can only be set on VPC instances // AWS will return an error of InvalidParameterCombination if we attempt
|
||||||
InstanceId: aws.String(d.Id()),
|
// to modify the source_dest_check of an instance in EC2 Classic
|
||||||
SourceDestCheck: &ec2.AttributeBooleanValue{
|
log.Printf("[INFO] Modifying `source_dest_check` on Instance %s", d.Id())
|
||||||
Value: aws.Bool(d.Get("source_dest_check").(bool)),
|
_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
||||||
},
|
InstanceId: aws.String(d.Id()),
|
||||||
})
|
SourceDestCheck: &ec2.AttributeBooleanValue{
|
||||||
if err != nil {
|
Value: aws.Bool(d.Get("source_dest_check").(bool)),
|
||||||
if ec2err, ok := err.(awserr.Error); ok {
|
},
|
||||||
// Toloerate InvalidParameterCombination error in Classic, otherwise
|
})
|
||||||
// return the error
|
if err != nil {
|
||||||
if "InvalidParameterCombination" != ec2err.Code() {
|
if ec2err, ok := err.(awserr.Error); ok {
|
||||||
return err
|
// Tolerate InvalidParameterCombination error in Classic, otherwise
|
||||||
|
// return the error
|
||||||
|
if "InvalidParameterCombination" != ec2err.Code() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[WARN] Attempted to modify SourceDestCheck on non VPC instance: %s", ec2err.Message())
|
||||||
}
|
}
|
||||||
log.Printf("[WARN] Attempted to modify SourceDestCheck on non VPC instance: %s", ec2err.Message())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1053,6 +1130,55 @@ func fetchRootDeviceName(ami string, conn *ec2.EC2) (*string, error) {
|
||||||
return rootDeviceName, nil
|
return rootDeviceName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildNetworkInterfaceOpts(d *schema.ResourceData, groups []*string, nInterfaces interface{}) []*ec2.InstanceNetworkInterfaceSpecification {
|
||||||
|
networkInterfaces := []*ec2.InstanceNetworkInterfaceSpecification{}
|
||||||
|
// Get necessary items
|
||||||
|
associatePublicIPAddress := d.Get("associate_public_ip_address").(bool)
|
||||||
|
subnet, hasSubnet := d.GetOk("subnet_id")
|
||||||
|
|
||||||
|
if hasSubnet && associatePublicIPAddress {
|
||||||
|
// If we have a non-default VPC / Subnet specified, we can flag
|
||||||
|
// AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided.
|
||||||
|
// You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise
|
||||||
|
// you get: Network interfaces and an instance-level subnet ID may not be specified on the same request
|
||||||
|
// You also need to attach Security Groups to the NetworkInterface instead of the instance,
|
||||||
|
// to avoid: Network interfaces and an instance-level security groups may not be specified on
|
||||||
|
// the same request
|
||||||
|
ni := &ec2.InstanceNetworkInterfaceSpecification{
|
||||||
|
AssociatePublicIpAddress: aws.Bool(associatePublicIPAddress),
|
||||||
|
DeviceIndex: aws.Int64(int64(0)),
|
||||||
|
SubnetId: aws.String(subnet.(string)),
|
||||||
|
Groups: groups,
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("private_ip"); ok {
|
||||||
|
ni.PrivateIpAddress = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
|
||||||
|
for _, v := range v.List() {
|
||||||
|
ni.Groups = append(ni.Groups, aws.String(v.(string)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
networkInterfaces = append(networkInterfaces, ni)
|
||||||
|
} else {
|
||||||
|
// If we have manually specified network interfaces, build and attach those here.
|
||||||
|
vL := nInterfaces.(*schema.Set).List()
|
||||||
|
for _, v := range vL {
|
||||||
|
ini := v.(map[string]interface{})
|
||||||
|
ni := &ec2.InstanceNetworkInterfaceSpecification{
|
||||||
|
DeviceIndex: aws.Int64(int64(ini["device_index"].(int))),
|
||||||
|
NetworkInterfaceId: aws.String(ini["network_interface_id"].(string)),
|
||||||
|
DeleteOnTermination: aws.Bool(ini["delete_on_termination"].(bool)),
|
||||||
|
}
|
||||||
|
networkInterfaces = append(networkInterfaces, ni)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return networkInterfaces
|
||||||
|
}
|
||||||
|
|
||||||
func readBlockDeviceMappingsFromConfig(
|
func readBlockDeviceMappingsFromConfig(
|
||||||
d *schema.ResourceData, conn *ec2.EC2) ([]*ec2.BlockDeviceMapping, error) {
|
d *schema.ResourceData, conn *ec2.EC2) ([]*ec2.BlockDeviceMapping, error) {
|
||||||
blockDevices := make([]*ec2.BlockDeviceMapping, 0)
|
blockDevices := make([]*ec2.BlockDeviceMapping, 0)
|
||||||
|
@ -1338,33 +1464,14 @@ func buildAwsInstanceOpts(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasSubnet && associatePublicIPAddress {
|
networkInterfaces, interfacesOk := d.GetOk("network_interface")
|
||||||
// If we have a non-default VPC / Subnet specified, we can flag
|
|
||||||
// AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided.
|
|
||||||
// You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise
|
|
||||||
// you get: Network interfaces and an instance-level subnet ID may not be specified on the same request
|
|
||||||
// You also need to attach Security Groups to the NetworkInterface instead of the instance,
|
|
||||||
// to avoid: Network interfaces and an instance-level security groups may not be specified on
|
|
||||||
// the same request
|
|
||||||
ni := &ec2.InstanceNetworkInterfaceSpecification{
|
|
||||||
AssociatePublicIpAddress: aws.Bool(associatePublicIPAddress),
|
|
||||||
DeviceIndex: aws.Int64(int64(0)),
|
|
||||||
SubnetId: aws.String(subnetID),
|
|
||||||
Groups: groups,
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := d.GetOk("private_ip"); ok {
|
// If setting subnet and public address, OR manual network interfaces, populate those now.
|
||||||
ni.PrivateIpAddress = aws.String(v.(string))
|
if hasSubnet && associatePublicIPAddress || interfacesOk {
|
||||||
}
|
// Otherwise we're attaching (a) network interface(s)
|
||||||
|
opts.NetworkInterfaces = buildNetworkInterfaceOpts(d, groups, networkInterfaces)
|
||||||
if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
|
|
||||||
for _, v := range v.List() {
|
|
||||||
ni.Groups = append(ni.Groups, aws.String(v.(string)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{ni}
|
|
||||||
} else {
|
} else {
|
||||||
|
// If simply specifying a subnetID, privateIP, Security Groups, or VPC Security Groups, build these now
|
||||||
if subnetID != "" {
|
if subnetID != "" {
|
||||||
opts.SubnetID = aws.String(subnetID)
|
opts.SubnetID = aws.String(subnetID)
|
||||||
}
|
}
|
||||||
|
@ -1397,7 +1504,6 @@ func buildAwsInstanceOpts(
|
||||||
if len(blockDevices) > 0 {
|
if len(blockDevices) > 0 {
|
||||||
opts.BlockDeviceMappings = blockDevices
|
opts.BlockDeviceMappings = blockDevices
|
||||||
}
|
}
|
||||||
|
|
||||||
return opts, nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -926,6 +926,58 @@ func TestAccAWSInstance_changeInstanceType(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccAWSInstance_primaryNetworkInterface(t *testing.T) {
|
||||||
|
var instance ec2.Instance
|
||||||
|
var ini ec2.NetworkInterface
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccInstanceConfigPrimaryNetworkInterface,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckInstanceExists("aws_instance.foo", &instance),
|
||||||
|
testAccCheckAWSENIExists("aws_network_interface.bar", &ini),
|
||||||
|
resource.TestCheckResourceAttr("aws_instance.foo", "network_interface.#", "1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccAWSInstance_addSecondaryInterface(t *testing.T) {
|
||||||
|
var before ec2.Instance
|
||||||
|
var after ec2.Instance
|
||||||
|
var iniPrimary ec2.NetworkInterface
|
||||||
|
var iniSecondary ec2.NetworkInterface
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccInstanceConfigAddSecondaryNetworkInterfaceBefore,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckInstanceExists("aws_instance.foo", &before),
|
||||||
|
testAccCheckAWSENIExists("aws_network_interface.primary", &iniPrimary),
|
||||||
|
resource.TestCheckResourceAttr("aws_instance.foo", "network_interface.#", "1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccInstanceConfigAddSecondaryNetworkInterfaceAfter,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckInstanceExists("aws_instance.foo", &after),
|
||||||
|
testAccCheckAWSENIExists("aws_network_interface.secondary", &iniSecondary),
|
||||||
|
resource.TestCheckResourceAttr("aws_instance.foo", "network_interface.#", "1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckInstanceNotRecreated(t *testing.T,
|
func testAccCheckInstanceNotRecreated(t *testing.T,
|
||||||
before, after *ec2.Instance) resource.TestCheckFunc {
|
before, after *ec2.Instance) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
|
@ -1696,3 +1748,129 @@ resource "aws_instance" "foo" {
|
||||||
subnet_id = "${aws_subnet.foo.id}"
|
subnet_id = "${aws_subnet.foo.id}"
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testAccInstanceConfigPrimaryNetworkInterface = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "172.16.0.0/16"
|
||||||
|
tags {
|
||||||
|
Name = "tf-instance-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
cidr_block = "172.16.10.0/24"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
tags {
|
||||||
|
Name = "tf-instance-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_network_interface" "bar" {
|
||||||
|
subnet_id = "${aws_subnet.foo.id}"
|
||||||
|
private_ips = ["172.16.10.100"]
|
||||||
|
tags {
|
||||||
|
Name = "primary_network_interface"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
ami = "ami-22b9a343"
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
network_interface {
|
||||||
|
network_interface_id = "${aws_network_interface.bar.id}"
|
||||||
|
device_index = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const testAccInstanceConfigAddSecondaryNetworkInterfaceBefore = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "172.16.0.0/16"
|
||||||
|
tags {
|
||||||
|
Name = "tf-instance-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
cidr_block = "172.16.10.0/24"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
tags {
|
||||||
|
Name = "tf-instance-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_network_interface" "primary" {
|
||||||
|
subnet_id = "${aws_subnet.foo.id}"
|
||||||
|
private_ips = ["172.16.10.100"]
|
||||||
|
tags {
|
||||||
|
Name = "primary_network_interface"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_network_interface" "secondary" {
|
||||||
|
subnet_id = "${aws_subnet.foo.id}"
|
||||||
|
private_ips = ["172.16.10.101"]
|
||||||
|
tags {
|
||||||
|
Name = "secondary_network_interface"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
ami = "ami-22b9a343"
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
network_interface {
|
||||||
|
network_interface_id = "${aws_network_interface.primary.id}"
|
||||||
|
device_index = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const testAccInstanceConfigAddSecondaryNetworkInterfaceAfter = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "172.16.0.0/16"
|
||||||
|
tags {
|
||||||
|
Name = "tf-instance-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
cidr_block = "172.16.10.0/24"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
tags {
|
||||||
|
Name = "tf-instance-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_network_interface" "primary" {
|
||||||
|
subnet_id = "${aws_subnet.foo.id}"
|
||||||
|
private_ips = ["172.16.10.100"]
|
||||||
|
tags {
|
||||||
|
Name = "primary_network_interface"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach previously created network interface, observe no state diff on instance resource
|
||||||
|
resource "aws_network_interface" "secondary" {
|
||||||
|
subnet_id = "${aws_subnet.foo.id}"
|
||||||
|
private_ips = ["172.16.10.101"]
|
||||||
|
tags {
|
||||||
|
Name = "secondary_network_interface"
|
||||||
|
}
|
||||||
|
attachment {
|
||||||
|
instance = "${aws_instance.foo.id}"
|
||||||
|
device_index = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
ami = "ami-22b9a343"
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
network_interface {
|
||||||
|
network_interface_id = "${aws_network_interface.primary.id}"
|
||||||
|
device_index = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
@ -87,6 +87,7 @@ instances. See [Shutdown Behavior](https://docs.aws.amazon.com/AWSEC2/latest/Use
|
||||||
instance. See [Block Devices](#block-devices) below for details.
|
instance. See [Block Devices](#block-devices) below for details.
|
||||||
* `ephemeral_block_device` - (Optional) Customize Ephemeral (also known as
|
* `ephemeral_block_device` - (Optional) Customize Ephemeral (also known as
|
||||||
"Instance Store") volumes on the instance. See [Block Devices](#block-devices) below for details.
|
"Instance Store") volumes on the instance. See [Block Devices](#block-devices) below for details.
|
||||||
|
* `network_interface` - (Optional) Customize network interfaces to be attached at instance boot time. See [Network Interfaces](#network-interfaces) below for more details.
|
||||||
|
|
||||||
|
|
||||||
## Block devices
|
## Block devices
|
||||||
|
@ -150,6 +151,59 @@ resources cannot be automatically detected by Terraform. After making updates
|
||||||
to block device configuration, resource recreation can be manually triggered by
|
to block device configuration, resource recreation can be manually triggered by
|
||||||
using the [`taint` command](/docs/commands/taint.html).
|
using the [`taint` command](/docs/commands/taint.html).
|
||||||
|
|
||||||
|
## Network Interfaces
|
||||||
|
|
||||||
|
Each of the `network_interface` blocks attach a network interface to an EC2 Instance during boot time. However, because
|
||||||
|
the network interface is attached at boot-time, replacing/modifying the network interface **WILL** trigger a recreation
|
||||||
|
of the EC2 Instance. If you should need at any point to detach/modify/re-attach a network interface to the instance, use
|
||||||
|
the `aws_network_interface` or `aws_network_interface_attachment` resources instead.
|
||||||
|
|
||||||
|
The `network_interface` configuration block _does_, however, allow users to supply their own network interface to be used
|
||||||
|
as the default network interface on an EC2 Instance, attached at `eth0`.
|
||||||
|
|
||||||
|
Each `network_interface` block supports the following:
|
||||||
|
|
||||||
|
* `device_index` - (Required) The integer index of the network interface attachment. Limited by instance type.
|
||||||
|
* `network_interface_id` - (Required) The ID of the network interface to attach.
|
||||||
|
* `delete_on_termination` - (Optional) Whether or not to delete the network interface on instance termination. Defaults to `false`.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "aws_vpc" "my_vpc" {
|
||||||
|
cidr_block = "172.16.0.0/16"
|
||||||
|
tags {
|
||||||
|
Name = "tf-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "my_subnet" {
|
||||||
|
vpc_id = "${aws_vpc.my_vpc.id}"
|
||||||
|
cidr_block = "172.16.10.0/24"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
tags {
|
||||||
|
Name = "tf-example"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_network_interface" "foo" {
|
||||||
|
subnet_id = "${aws_subnet.my_subnet.id}"
|
||||||
|
private_ips = ["172.16.10.100"]
|
||||||
|
tags {
|
||||||
|
Name = "primary_network_interface"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
ami = "ami-22b9a343" // us-west-2
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
network_interface {
|
||||||
|
network_interface_id = "${aws_network_interface.foo.id}"
|
||||||
|
device_index = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Attributes Reference
|
## Attributes Reference
|
||||||
|
|
||||||
The following attributes are exported:
|
The following attributes are exported:
|
||||||
|
@ -162,6 +216,7 @@ The following attributes are exported:
|
||||||
is only available if you've enabled DNS hostnames for your VPC
|
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.
|
* `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.
|
||||||
* `network_interface_id` - The ID of the network interface that was created with the instance.
|
* `network_interface_id` - The ID of the network interface that was created with the instance.
|
||||||
|
* `primary_network_interface_id` - The ID of the instance's primary network interface.
|
||||||
* `private_dns` - The private DNS name assigned to the instance. Can only be
|
* `private_dns` - The private DNS name assigned to the instance. Can only be
|
||||||
used inside the Amazon EC2, and only available if you've enabled DNS hostnames
|
used inside the Amazon EC2, and only available if you've enabled DNS hostnames
|
||||||
for your VPC
|
for your VPC
|
||||||
|
|
Loading…
Reference in New Issue