Skip to content

Commit 8380a7b

Browse files
committed
provider/aws: Allow multiple EIPs to associate to single ENI
When calling AssociateAddress, the PrivateIpAddress parameter must be used to select which private IP the EIP should associate with, otherwise the EIP always associates with the _first_ private IP. Without this parameter, multiple EIPs couldn't be assigned to a single ENI. Includes covering test and docs update. Fixes hashicorp#2997
1 parent 7eb9175 commit 8380a7b

3 files changed

Lines changed: 86 additions & 3 deletions

File tree

builtin/providers/aws/resource_aws_eip.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func resourceAwsEip() *schema.Resource {
6161

6262
"private_ip": &schema.Schema{
6363
Type: schema.TypeString,
64+
Optional: true,
6465
Computed: true,
6566
},
6667
},
@@ -180,10 +181,15 @@ func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error {
180181

181182
// more unique ID conditionals
182183
if domain == "vpc" {
184+
var privateIpAddress *string
185+
if v := d.Get("private_ip").(string); v != "" {
186+
privateIpAddress = aws.String(v)
187+
}
183188
assocOpts = &ec2.AssociateAddressInput{
184189
NetworkInterfaceId: aws.String(networkInterfaceId),
185190
InstanceId: aws.String(instanceId),
186191
AllocationId: aws.String(d.Id()),
192+
PrivateIpAddress: privateIpAddress,
187193
}
188194
}
189195

builtin/providers/aws/resource_aws_eip_test.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,29 @@ func TestAccAWSEIP_network_interface(t *testing.T) {
7878
})
7979
}
8080

81+
func TestAccAWSEIP_twoEIPsOneNetworkInterface(t *testing.T) {
82+
var one, two ec2.Address
83+
84+
resource.Test(t, resource.TestCase{
85+
PreCheck: func() { testAccPreCheck(t) },
86+
Providers: testAccProviders,
87+
CheckDestroy: testAccCheckAWSEIPDestroy,
88+
Steps: []resource.TestStep{
89+
resource.TestStep{
90+
Config: testAccAWSEIPMultiNetworkInterfaceConfig,
91+
Check: resource.ComposeTestCheckFunc(
92+
testAccCheckAWSEIPExists("aws_eip.one", &one),
93+
testAccCheckAWSEIPAttributes(&one),
94+
testAccCheckAWSEIPAssociated(&one),
95+
testAccCheckAWSEIPExists("aws_eip.two", &two),
96+
testAccCheckAWSEIPAttributes(&two),
97+
testAccCheckAWSEIPAssociated(&two),
98+
),
99+
},
100+
},
101+
})
102+
}
103+
81104
func testAccCheckAWSEIPDestroy(s *terraform.State) error {
82105
conn := testAccProvider.Meta().(*AWSClient).ec2conn
83106

@@ -136,7 +159,7 @@ func testAccCheckAWSEIPAttributes(conf *ec2.Address) resource.TestCheckFunc {
136159

137160
func testAccCheckAWSEIPAssociated(conf *ec2.Address) resource.TestCheckFunc {
138161
return func(s *terraform.State) error {
139-
if *conf.AssociationId == "" {
162+
if conf.AssociationId == nil || *conf.AssociationId == "" {
140163
return fmt.Errorf("empty association_id")
141164
}
142165

@@ -220,6 +243,7 @@ resource "aws_eip" "bar" {
220243
instance = "${aws_instance.bar.id}"
221244
}
222245
`
246+
223247
const testAccAWSEIPNetworkInterfaceConfig = `
224248
resource "aws_vpc" "bar" {
225249
cidr_block = "10.0.0.0/24"
@@ -242,3 +266,32 @@ resource "aws_eip" "bar" {
242266
network_interface = "${aws_network_interface.bar.id}"
243267
}
244268
`
269+
270+
const testAccAWSEIPMultiNetworkInterfaceConfig = `
271+
resource "aws_vpc" "bar" {
272+
cidr_block = "10.0.0.0/24"
273+
}
274+
resource "aws_internet_gateway" "bar" {
275+
vpc_id = "${aws_vpc.bar.id}"
276+
}
277+
resource "aws_subnet" "bar" {
278+
vpc_id = "${aws_vpc.bar.id}"
279+
availability_zone = "us-west-2a"
280+
cidr_block = "10.0.0.0/24"
281+
}
282+
resource "aws_network_interface" "bar" {
283+
subnet_id = "${aws_subnet.bar.id}"
284+
private_ips = ["10.0.0.10", "10.0.0.11"]
285+
security_groups = [ "${aws_vpc.bar.default_security_group_id}" ]
286+
}
287+
resource "aws_eip" "one" {
288+
vpc = "true"
289+
network_interface = "${aws_network_interface.bar.id}"
290+
private_ip = "10.0.0.10"
291+
}
292+
resource "aws_eip" "two" {
293+
vpc = "true"
294+
network_interface = "${aws_network_interface.bar.id}"
295+
private_ip = "10.0.0.11"
296+
}
297+
`

website/source/docs/providers/aws/r/eip.html.markdown

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,31 @@ Provides an Elastic IP resource.
1212

1313
## Example Usage
1414

15+
Single EIP associated with an instance:
16+
1517
```
1618
resource "aws_eip" "lb" {
17-
instance = "${aws_instance.web.id}"
18-
vpc = true
19+
instance = "${aws_instance.web.id}"
20+
vpc = true
21+
}
22+
```
23+
24+
Muliple EIPs associated with a single network interface:
25+
26+
```
27+
resource "aws_network_interface" "multi-ip" {
28+
subnet_id = "${aws_subnet.main.id}"
29+
private_ips = ["10.0.0.10", "10.0.0.11"]
30+
}
31+
resource "aws_eip" "one" {
32+
vpc = true
33+
network_interface = "${aws_network_interface.multi-ip.id}"
34+
private_ip = "10.0.0.10"
35+
}
36+
resource "aws_eip" "two" {
37+
vpc = true
38+
network_interface = "${aws_network_interface.multi-ip.id}"
39+
private_ip = "10.0.0.11"
1940
}
2041
```
2142

@@ -26,6 +47,9 @@ The following arguments are supported:
2647
* `vpc` - (Optional) Boolean if the EIP is in a VPC or not.
2748
* `instance` - (Optional) EC2 instance ID.
2849
* `network_interface` - (Optional) Network interface ID to associate with.
50+
* `private_ip` - (Optional) The primary or secondary private IP address to
51+
associate with the Elastic IP address. If no private IP address is specified,
52+
the Elastic IP address is associated with the primary private IP address.
2953

3054
~> **NOTE:** You can specify either the `instance` ID or the `network_interface` ID,
3155
but not both. Including both will **not** return an error from the AWS API, but will

0 commit comments

Comments
 (0)