Skip to content

Commit

Permalink
service/ec2: Add customer_owned_ipv4_pool and map_customer_owned_ip_o…
Browse files Browse the repository at this point in the history
…n_launch attributes to aws_subnet data source and resource

Reference: #13170
Reference: #13171

Output from acceptance testing:

```
Pending us-west-2 deployment of EC2 API.
```
  • Loading branch information
bflad committed Jun 16, 2020
1 parent 844f6b9 commit ace6c32
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 1 deletion.
12 changes: 12 additions & 0 deletions aws/data_source_aws_subnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ func dataSourceAwsSubnet() *schema.Resource {
Computed: true,
},

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

"map_customer_owned_ip_on_launch": {
Type: schema.TypeBool,
Computed: true,
},

"map_public_ip_on_launch": {
Type: schema.TypeBool,
Computed: true,
Expand Down Expand Up @@ -180,6 +190,8 @@ func dataSourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error {
}

d.Set("assign_ipv6_address_on_creation", subnet.AssignIpv6AddressOnCreation)
d.Set("customer_owned_ipv4_pool", subnet.CustomerOwnedIpv4Pool)
d.Set("map_customer_owned_ip_on_launch", subnet.MapCustomerOwnedIpOnLaunch)
d.Set("map_public_ip_on_launch", subnet.MapPublicIpOnLaunch)

for _, a := range subnet.Ipv6CidrBlockAssociationSet {
Expand Down
96 changes: 96 additions & 0 deletions aws/data_source_aws_subnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aws

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
Expand Down Expand Up @@ -38,6 +39,8 @@ func TestAccDataSourceAwsSubnet_basic(t *testing.T) {
resource.TestCheckResourceAttr(ds1ResourceName, "cidr_block", cidr),
resource.TestCheckResourceAttr(ds1ResourceName, "tags.Name", tag),
resource.TestCheckResourceAttrPair(ds1ResourceName, "arn", snResourceName, "arn"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "customer_owned_ipv4_pool", snResourceName, "customer_owned_ipv4_pool"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "map_customer_owned_ip_on_launch", snResourceName, "map_customer_owned_ip_on_launch"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "outpost_arn", snResourceName, "outpost_arn"),

resource.TestCheckResourceAttrPair(ds2ResourceName, "id", snResourceName, "id"),
Expand All @@ -48,6 +51,8 @@ func TestAccDataSourceAwsSubnet_basic(t *testing.T) {
resource.TestCheckResourceAttr(ds2ResourceName, "cidr_block", cidr),
resource.TestCheckResourceAttr(ds2ResourceName, "tags.Name", tag),
resource.TestCheckResourceAttrPair(ds2ResourceName, "arn", snResourceName, "arn"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "customer_owned_ipv4_pool", snResourceName, "customer_owned_ipv4_pool"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "map_customer_owned_ip_on_launch", snResourceName, "map_customer_owned_ip_on_launch"),
resource.TestCheckResourceAttrPair(ds2ResourceName, "outpost_arn", snResourceName, "outpost_arn"),

resource.TestCheckResourceAttrPair(ds3ResourceName, "id", snResourceName, "id"),
Expand All @@ -58,6 +63,8 @@ func TestAccDataSourceAwsSubnet_basic(t *testing.T) {
resource.TestCheckResourceAttr(ds3ResourceName, "cidr_block", cidr),
resource.TestCheckResourceAttr(ds3ResourceName, "tags.Name", tag),
resource.TestCheckResourceAttrPair(ds3ResourceName, "arn", snResourceName, "arn"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "customer_owned_ipv4_pool", snResourceName, "customer_owned_ipv4_pool"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "map_customer_owned_ip_on_launch", snResourceName, "map_customer_owned_ip_on_launch"),
resource.TestCheckResourceAttrPair(ds3ResourceName, "outpost_arn", snResourceName, "outpost_arn"),

resource.TestCheckResourceAttrPair(ds4ResourceName, "id", snResourceName, "id"),
Expand All @@ -68,6 +75,8 @@ func TestAccDataSourceAwsSubnet_basic(t *testing.T) {
resource.TestCheckResourceAttr(ds4ResourceName, "cidr_block", cidr),
resource.TestCheckResourceAttr(ds4ResourceName, "tags.Name", tag),
resource.TestCheckResourceAttrPair(ds4ResourceName, "arn", snResourceName, "arn"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "customer_owned_ipv4_pool", snResourceName, "customer_owned_ipv4_pool"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "map_customer_owned_ip_on_launch", snResourceName, "map_customer_owned_ip_on_launch"),
resource.TestCheckResourceAttrPair(ds4ResourceName, "outpost_arn", snResourceName, "outpost_arn"),

resource.TestCheckResourceAttrPair(ds5ResourceName, "id", snResourceName, "id"),
Expand All @@ -78,6 +87,8 @@ func TestAccDataSourceAwsSubnet_basic(t *testing.T) {
resource.TestCheckResourceAttr(ds5ResourceName, "cidr_block", cidr),
resource.TestCheckResourceAttr(ds5ResourceName, "tags.Name", tag),
resource.TestCheckResourceAttrPair(ds5ResourceName, "arn", snResourceName, "arn"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "customer_owned_ipv4_pool", snResourceName, "customer_owned_ipv4_pool"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "map_customer_owned_ip_on_launch", snResourceName, "map_customer_owned_ip_on_launch"),
resource.TestCheckResourceAttrPair(ds5ResourceName, "outpost_arn", snResourceName, "outpost_arn"),

resource.TestCheckResourceAttrPair(ds6ResourceName, "id", snResourceName, "id"),
Expand All @@ -88,6 +99,8 @@ func TestAccDataSourceAwsSubnet_basic(t *testing.T) {
resource.TestCheckResourceAttr(ds6ResourceName, "cidr_block", cidr),
resource.TestCheckResourceAttr(ds6ResourceName, "tags.Name", tag),
resource.TestCheckResourceAttrPair(ds6ResourceName, "arn", snResourceName, "arn"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "customer_owned_ipv4_pool", snResourceName, "customer_owned_ipv4_pool"),
resource.TestCheckResourceAttrPair(ds1ResourceName, "map_customer_owned_ip_on_launch", snResourceName, "map_customer_owned_ip_on_launch"),
resource.TestCheckResourceAttrPair(ds6ResourceName, "outpost_arn", snResourceName, "outpost_arn"),
),
},
Expand Down Expand Up @@ -134,6 +147,45 @@ func TestAccDataSourceAwsSubnet_ipv6ByIpv6CidrBlock(t *testing.T) {
})
}

func TestAccDataSourceAwsSubnet_CustomerOwnedIpv4Pool(t *testing.T) {
// Hide Outposts testing behind consistent environment variable
outpostArn := os.Getenv("AWS_OUTPOST_ARN")
if outpostArn == "" {
t.Skip(
"Environment variable AWS_OUTPOST_ARN is not set. " +
"This environment variable must be set to the ARN of " +
"a deployed Outpost to enable this test.")
}

// Local Gateway Route Table ID filtering in DescribeCoipPools is not currently working
poolId := os.Getenv("AWS_COIP_POOL_ID")
if poolId == "" {
t.Skip(
"Environment variable AWS_COIP_POOL_ID is not set. " +
"This environment variable must be set to the ID of " +
"a deployed Coip Pool to enable this test.")
}

dataSourceName := "data.aws_subnet.test"
resourceName := "aws_subnet.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: resourceName,
Providers: testAccProviders,
CheckDestroy: testAccCheckSubnetDestroy,
Steps: []resource.TestStep{
{
Config: testAccDataSourceSubnetConfigCustomerOwnedIpv4Pool(outpostArn, poolId),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(resourceName, "customer_owned_ipv4_pool", dataSourceName, "customer_owned_ipv4_pool"),
resource.TestCheckResourceAttrPair(resourceName, "map_customer_owned_ip_on_launch", dataSourceName, "map_customer_owned_ip_on_launch"),
),
},
},
})
}

func testAccDataSourceAwsSubnetConfig(rInt int) string {
return fmt.Sprintf(`
data "aws_availability_zones" "available" {
Expand Down Expand Up @@ -307,3 +359,47 @@ data "aws_subnet" "by_ipv6_cidr" {
}
`, rInt, rInt)
}

func testAccDataSourceSubnetConfigCustomerOwnedIpv4Pool(outpostArn string, customerOwnedIpv4Pool string) string {
return fmt.Sprintf(`
# TODO: This should be replaced with aws_outposts_outpost data source, when available.
data "aws_availability_zones" "current" {
# Exclude Availability Zones with limited Outposts functionality.
blacklisted_zone_ids = ["usw2-az4"]
# Exclude Local Zones
filter {
name = "opt-in-status"
values = ["opt-in-not-required"]
}
}
data "aws_ec2_coip_pool" "test" {
pool_id = %[2]q
}
resource "aws_vpc" "test" {
cidr_block = "10.1.0.0/16"
tags = {
Name = "tf-acc-test-subnet-customer-owned-ipv4-pool"
}
}
resource "aws_subnet" "test" {
availability_zone = data.aws_availability_zones.current.names[0]
cidr_block = "10.1.1.0/24"
customer_owned_ipv4_pool = data.aws_ec2_coip_pool.test.id
outpost_arn = %[1]q
vpc_id = aws_vpc.test.id
tags = {
Name = aws_vpc.test.tags["Name"]
}
}
data "aws_subnet" "test" {
id = aws_subnet.test.id
}
`, outpostArn, customerOwnedIpv4Pool)
}
63 changes: 63 additions & 0 deletions aws/resource_aws_subnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ func resourceAwsSubnet() *schema.Resource {
ConflictsWith: []string{"availability_zone"},
},

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

"map_customer_owned_ip_on_launch": {
Type: schema.TypeBool,
Optional: true,
},

"map_public_ip_on_launch": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -175,6 +185,30 @@ func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error {
}
}

if v, ok := d.GetOk("customer_owned_ipv4_pool"); ok {
input := &ec2.ModifySubnetAttributeInput{
CustomerOwnedIpv4Pool: aws.String(v.(string)),
SubnetId: aws.String(d.Id()),
}

if _, err := conn.ModifySubnetAttribute(input); err != nil {
return fmt.Errorf("error setting EC2 Subnet (%s) customer owned IPv4 pool: %w", d.Id(), err)
}
}

if d.Get("map_customer_owned_ip_on_launch").(bool) {
input := &ec2.ModifySubnetAttributeInput{
MapCustomerOwnedIpOnLaunch: &ec2.AttributeBooleanValue{
Value: aws.Bool(true),
},
SubnetId: aws.String(d.Id()),
}

if _, err := conn.ModifySubnetAttribute(input); err != nil {
return fmt.Errorf("error enabling EC2 Subnet (%s) map customer owned IP on launch: %w", d.Id(), err)
}
}

if d.Get("map_public_ip_on_launch").(bool) {
input := &ec2.ModifySubnetAttributeInput{
MapPublicIpOnLaunch: &ec2.AttributeBooleanValue{
Expand Down Expand Up @@ -217,6 +251,8 @@ func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error {
d.Set("availability_zone", subnet.AvailabilityZone)
d.Set("availability_zone_id", subnet.AvailabilityZoneId)
d.Set("cidr_block", subnet.CidrBlock)
d.Set("customer_owned_ipv4_pool", subnet.CustomerOwnedIpv4Pool)
d.Set("map_customer_owned_ip_on_launch", subnet.MapCustomerOwnedIpOnLaunch)
d.Set("map_public_ip_on_launch", subnet.MapPublicIpOnLaunch)
d.Set("assign_ipv6_address_on_creation", subnet.AssignIpv6AddressOnCreation)
d.Set("outpost_arn", subnet.OutpostArn)
Expand Down Expand Up @@ -255,6 +291,33 @@ func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error {
}
}

// You cannot modify multiple subnet attributes in the same request.
// Reference: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifySubnetAttribute.html

if d.HasChange("customer_owned_ipv4_pool") {
input := &ec2.ModifySubnetAttributeInput{
CustomerOwnedIpv4Pool: aws.String(d.Get("customer_owned_ipv4_pool").(string)),
SubnetId: aws.String(d.Id()),
}

if _, err := conn.ModifySubnetAttribute(input); err != nil {
return fmt.Errorf("error updating EC2 Subnet (%s) customer owned IPv4 pool: %w", d.Id(), err)
}
}

if d.HasChange("map_customer_owned_ip_on_launch") {
input := &ec2.ModifySubnetAttributeInput{
MapCustomerOwnedIpOnLaunch: &ec2.AttributeBooleanValue{
Value: aws.Bool(d.Get("map_customer_owned_ip_on_launch").(bool)),
},
SubnetId: aws.String(d.Id()),
}

if _, err := conn.ModifySubnetAttribute(input); err != nil {
return fmt.Errorf("error updating EC2 Subnet (%s) map customer owned IP on launch: %w", d.Id(), err)
}
}

if d.HasChange("map_public_ip_on_launch") {
modifyOpts := &ec2.ModifySubnetAttributeInput{
SubnetId: aws.String(d.Id()),
Expand Down
Loading

0 comments on commit ace6c32

Please sign in to comment.