Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supporting EIPs with specified BYOIP addresses #8876

Merged
merged 10 commits into from
Apr 28, 2021
3 changes: 3 additions & 0 deletions .changelog/8876.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_eip: Add `address` argument to recover or an IPv4 address from an address pool, supporting BYOIP
```
76 changes: 37 additions & 39 deletions aws/resource_aws_eip.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,96 +41,86 @@ func resourceAwsEip() *schema.Resource {
},

Schema: map[string]*schema.Schema{
"vpc": {
Type: schema.TypeBool,
"address": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},

"instance": {
"allocation_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"network_interface": {
"associate_with_private_ip": {
Type: schema.TypeString,
Optional: true,
},
"association_id": {
Type: schema.TypeString,
Computed: true,
},

"allocation_id": {
"carrier_ip": {
Type: schema.TypeString,
Computed: true,
},

"association_id": {
"customer_owned_ip": {
Type: schema.TypeString,
Computed: true,
},

"customer_owned_ipv4_pool": {
Type: schema.TypeString,
Optional: true,
},
"domain": {
Type: schema.TypeString,
Computed: true,
},

"public_ip": {
"instance": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"public_dns": {
"network_border_group": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},

"private_ip": {
"network_interface": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"private_dns": {
Type: schema.TypeString,
Computed: true,
},

"associate_with_private_ip": {
Type: schema.TypeString,
Optional: true,
},

"carrier_ip": {
"private_ip": {
Type: schema.TypeString,
Computed: true,
},

"customer_owned_ipv4_pool": {
"public_dns": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"customer_owned_ip": {
"public_ip": {
Type: schema.TypeString,
Computed: true,
},

"public_ipv4_pool": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},

"network_border_group": {
Type: schema.TypeString,
"tags": tagsSchema(),
"tags_all": tagsSchemaComputed(),
"vpc": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
ForceNew: true,
Computed: true,
},

"tags": tagsSchema(),
"tags_all": tagsSchemaComputed(),
},
}
}
Expand Down Expand Up @@ -158,6 +148,14 @@ func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error {
allocOpts.TagSpecifications = ec2TagSpecificationsFromKeyValueTags(tags, ec2.ResourceTypeElasticIp)
}

if v, ok := d.GetOk("address"); ok {
supportedPlatforms := meta.(*AWSClient).supportedplatforms
if domainOpt != ec2.DomainTypeVpc && len(supportedPlatforms) > 0 && hasEc2Classic(supportedPlatforms) {
return fmt.Errorf("error, address to recover cannot be set for a standard-domain EIP - must be a VPC-domain EIP")
}
allocOpts.Address = aws.String(v.(string))
}

if v, ok := d.GetOk("public_ipv4_pool"); ok {
allocOpts.PublicIpv4Pool = aws.String(v.(string))
}
Expand Down
121 changes: 118 additions & 3 deletions aws/resource_aws_eip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func TestAccAWSEIP_Instance(t *testing.T) {
CheckDestroy: testAccCheckAWSEIPDestroy,
Steps: []resource.TestStep{
{
Config: testAccEIPInstanceConfig(),
Config: testAccAWSEIPInstanceConfig(),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEIPExists(resourceName, false, &conf),
testAccCheckAWSEIPAttributes(&conf),
Expand Down Expand Up @@ -660,6 +660,96 @@ func TestAccAWSEIP_carrierIP(t *testing.T) {
})
}

func TestAccAWSEIP_BYOIPAddress_default(t *testing.T) {
// Test case address not set
var conf ec2.Address
resourceName := "aws_eip.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID),
IDRefreshName: resourceName,
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEIPDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEIPConfig_BYOIPAddress_custom_default,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEIPExists(resourceName, false, &conf),
testAccCheckAWSEIPAttributes(&conf),
),
},
},
})
}

func TestAccAWSEIP_BYOIPAddress_custom(t *testing.T) {
// Test Case for address being set

if os.Getenv("AWS_EC2_EIP_BYOIP_ADDRESS") == "" {
t.Skip("Environment variable AWS_EC2_EIP_BYOIP_ADDRESS is not set")
}

var conf ec2.Address
resourceName := "aws_eip.test"

address := os.Getenv("AWS_EC2_EIP_BYOIP_ADDRESS")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID),
IDRefreshName: resourceName,
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEIPDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEIPConfig_BYOIPAddress_custom(address),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEIPExists(resourceName, false, &conf),
testAccCheckAWSEIPAttributes(&conf),
resource.TestCheckResourceAttr(resourceName, "public_ip", address),
),
},
},
})
}

func TestAccAWSEIP_BYOIPAddress_custom_with_PublicIpv4Pool(t *testing.T) {
// Test Case for both address and public_ipv4_pool being set
if os.Getenv("AWS_EC2_EIP_BYOIP_ADDRESS") == "" {
t.Skip("Environment variable AWS_EC2_EIP_BYOIP_ADDRESS is not set")
}

if os.Getenv("AWS_EC2_EIP_PUBLIC_IPV4_POOL") == "" {
t.Skip("Environment variable AWS_EC2_EIP_PUBLIC_IPV4_POOL is not set")
}

var conf ec2.Address
resourceName := "aws_eip.test"

address := os.Getenv("AWS_EC2_EIP_BYOIP_ADDRESS")
poolName := os.Getenv("AWS_EC2_EIP_PUBLIC_IPV4_POOL")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID),
IDRefreshName: resourceName,
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEIPDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEIPConfig_BYOIPAddress_custom_with_PublicIpv4Pool(address, poolName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEIPExists(resourceName, false, &conf),
testAccCheckAWSEIPAttributes(&conf),
resource.TestCheckResourceAttr(resourceName, "public_ip", address),
resource.TestCheckResourceAttr(resourceName, "public_ipv4_pool", poolName),
),
},
},
})
}

func testAccCheckAWSEIPDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn

Expand Down Expand Up @@ -907,7 +997,7 @@ func testAccEIPPublicIPv4PoolCustomConfig(poolName string) string {
return fmt.Sprintf(`
resource "aws_eip" "test" {
vpc = true
public_ipv4_pool = "%s"
public_ipv4_pool = %[1]q
}
`, poolName)
}
Expand All @@ -929,7 +1019,32 @@ resource "aws_eip" "test" {
`)
}

func testAccEIPInstanceConfig() string {
const testAccAWSEIPConfig_BYOIPAddress_custom_default = `
resource "aws_eip" "test" {
vpc = true
}
`

func testAccAWSEIPConfig_BYOIPAddress_custom(address string) string {
return fmt.Sprintf(`
resource "aws_eip" "test" {
vpc = true
address = %[1]q
}
`, address)
}

func testAccAWSEIPConfig_BYOIPAddress_custom_with_PublicIpv4Pool(address string, poolname string) string {
return fmt.Sprintf(`
resource "aws_eip" "test" {
vpc = true
address = %[1]q
public_ipv4_pool = %[2]q
}
`, address, poolname)
}

func testAccAWSEIPInstanceConfig() string {
return composeConfig(
testAccLatestAmazonLinuxHvmEbsAmiConfig(),
testAccAvailableEc2InstanceTypeForAvailabilityZone("aws_subnet.test.availability_zone", "t3.micro", "t2.micro"),
Expand Down
12 changes: 8 additions & 4 deletions website/docs/r/eip.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Provides an Elastic IP resource.

## Example Usage

Single EIP associated with an instance:
### Single EIP associated with an instance

```terraform
resource "aws_eip" "lb" {
Expand All @@ -25,7 +25,7 @@ resource "aws_eip" "lb" {
}
```

Multiple EIPs associated with a single network interface:
### Multiple EIPs associated with a single network interface

```terraform
resource "aws_network_interface" "multi-ip" {
Expand All @@ -46,7 +46,7 @@ resource "aws_eip" "two" {
}
```

Attaching an EIP to an Instance with a pre-assigned private ip (VPC Only):
### Attaching an EIP to an Instance with a pre-assigned private ip (VPC Only)

```terraform
resource "aws_vpc" "default" {
Expand Down Expand Up @@ -84,7 +84,7 @@ resource "aws_eip" "bar" {
}
```

Allocating EIP from the BYOIP pool:
### Allocating EIP from the BYOIP pool

```terraform
resource "aws_eip" "byoip-ip" {
Expand All @@ -97,6 +97,7 @@ resource "aws_eip" "byoip-ip" {

The following arguments are supported:

* `address` - (Optional) IP address from an EC2 BYOIP pool. This option is only available for VPC EIPs.
* `associate_with_private_ip` - (Optional) User-specified primary or secondary private IP address to associate with the Elastic IP address. If no private IP address is specified, the Elastic IP address is associated with the primary private IP address.
* `customer_owned_ipv4_pool` - (Optional) ID of a customer-owned address pool. For more on customer owned IP addressed check out [Customer-owned IP addresses guide](https://docs.aws.amazon.com/outposts/latest/userguide/outposts-networking-components.html#ip-addressing).
* `instance` - (Optional) EC2 instance ID.
Expand All @@ -108,6 +109,9 @@ The following arguments are supported:

~> **NOTE:** You can specify either the `instance` ID or the `network_interface` ID, but not both. Including both will **not** return an error from the AWS API, but will have undefined behavior. See the relevant [AssociateAddress API Call][1] for more information.

~> **NOTE:** Specifying both `public_ipv4_pool` and `address` won't cause an error but `address` will be used in the
case both options are defined as the api only requires one or the other.

## Attributes Reference

In addition to all arguments above, the following attributes are exported:
Expand Down