provider/aws: Implement IPV6 Support for ec2 / VPC (#10538)
* provider/aws: Add support for IPV6 enabled VPC ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSVpc' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/12/09 14:07:31 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSVpc -timeout 120m === RUN TestAccAWSVpc_importBasic --- PASS: TestAccAWSVpc_importBasic (43.03s) === RUN TestAccAWSVpc_basic --- PASS: TestAccAWSVpc_basic (36.32s) === RUN TestAccAWSVpc_enableIpv6 --- PASS: TestAccAWSVpc_enableIpv6 (29.37s) === RUN TestAccAWSVpc_dedicatedTenancy --- PASS: TestAccAWSVpc_dedicatedTenancy (36.63s) === RUN TestAccAWSVpc_tags --- PASS: TestAccAWSVpc_tags (67.54s) === RUN TestAccAWSVpc_update --- PASS: TestAccAWSVpc_update (66.16s) === RUN TestAccAWSVpc_bothDnsOptionsSet --- PASS: TestAccAWSVpc_bothDnsOptionsSet (16.82s) === RUN TestAccAWSVpc_DisabledDnsSupport --- PASS: TestAccAWSVpc_DisabledDnsSupport (36.52s) === RUN TestAccAWSVpc_classiclinkOptionSet --- PASS: TestAccAWSVpc_classiclinkOptionSet (38.13s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 739.543s ``` * provider/aws: New Resource: aws_egress_only_internet_gateway ``` make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSEgressOnlyInternetGateway_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/12/09 14:22:16 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSEgressOnlyInternetGateway_ -timeout 120m === RUN TestAccAWSEgressOnlyInternetGateway_basic --- PASS: TestAccAWSEgressOnlyInternetGateway_basic (32.67s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 32.692s ``` * provider/aws: Add IPV6 support to aws_subnet ``` % make testacc TEST=./builtin/providers/aws % TESTARGS='-run=TestAccAWSSubnet_' % 1 ↵ ✹ ✭ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/02/27 19:08:34 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSSubnet_ -timeout 120m === RUN TestAccAWSSubnet_importBasic --- PASS: TestAccAWSSubnet_importBasic (69.88s) === RUN TestAccAWSSubnet_basic --- PASS: TestAccAWSSubnet_basic (51.28s) === RUN TestAccAWSSubnet_ipv6 --- PASS: TestAccAWSSubnet_ipv6 (90.39s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws211.574s ``` * provider/aws: Add support for running aws_instances with ipv6 addresses
This commit is contained in:
parent
3d198295f2
commit
177400dbbf
|
@ -275,6 +275,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_ecs_task_definition": resourceAwsEcsTaskDefinition(),
|
"aws_ecs_task_definition": resourceAwsEcsTaskDefinition(),
|
||||||
"aws_efs_file_system": resourceAwsEfsFileSystem(),
|
"aws_efs_file_system": resourceAwsEfsFileSystem(),
|
||||||
"aws_efs_mount_target": resourceAwsEfsMountTarget(),
|
"aws_efs_mount_target": resourceAwsEfsMountTarget(),
|
||||||
|
"aws_egress_only_internet_gateway": resourceAwsEgressOnlyInternetGateway(),
|
||||||
"aws_eip": resourceAwsEip(),
|
"aws_eip": resourceAwsEip(),
|
||||||
"aws_eip_association": resourceAwsEipAssociation(),
|
"aws_eip_association": resourceAwsEipAssociation(),
|
||||||
"aws_elasticache_cluster": resourceAwsElasticacheCluster(),
|
"aws_elasticache_cluster": resourceAwsElasticacheCluster(),
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsEgressOnlyInternetGateway() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsEgressOnlyInternetGatewayCreate,
|
||||||
|
Read: resourceAwsEgressOnlyInternetGatewayRead,
|
||||||
|
Delete: resourceAwsEgressOnlyInternetGatewayDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"vpc_id": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEgressOnlyInternetGatewayCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).ec2conn
|
||||||
|
|
||||||
|
resp, err := conn.CreateEgressOnlyInternetGateway(&ec2.CreateEgressOnlyInternetGatewayInput{
|
||||||
|
VpcId: aws.String(d.Get("vpc_id").(string)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating egress internet gateway: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(*resp.EgressOnlyInternetGateway.EgressOnlyInternetGatewayId)
|
||||||
|
|
||||||
|
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
|
||||||
|
igRaw, _, err := EIGWStateRefreshFunc(conn, d.Id())()
|
||||||
|
if igRaw != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return resource.RetryableError(err)
|
||||||
|
} else {
|
||||||
|
return resource.NonRetryableError(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errwrap.Wrapf("{{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsEgressOnlyInternetGatewayRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EIGWStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
resp, err := conn.DescribeEgressOnlyInternetGateways(&ec2.DescribeEgressOnlyInternetGatewaysInput{
|
||||||
|
EgressOnlyInternetGatewayIds: []*string{aws.String(id)},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec2err, ok := err.(awserr.Error)
|
||||||
|
if ok && ec2err.Code() == "InvalidEgressInternetGatewayID.NotFound" {
|
||||||
|
resp = nil
|
||||||
|
} else {
|
||||||
|
log.Printf("[ERROR] Error on EIGWStateRefreshFunc: %s", err)
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp == nil {
|
||||||
|
// Sometimes AWS just has consistency issues and doesn't see
|
||||||
|
// our instance yet. Return an empty state.
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ig := resp.EgressOnlyInternetGateways[0]
|
||||||
|
return ig, "available", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEgressOnlyInternetGatewayRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).ec2conn
|
||||||
|
|
||||||
|
resp, err := conn.DescribeEgressOnlyInternetGateways(&ec2.DescribeEgressOnlyInternetGatewaysInput{
|
||||||
|
EgressOnlyInternetGatewayIds: []*string{aws.String(d.Id())},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error describing egress internet gateway: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, igw := range resp.EgressOnlyInternetGateways {
|
||||||
|
if *igw.EgressOnlyInternetGatewayId == d.Id() {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
log.Printf("[Error] Cannot find Egress Only Internet Gateway: %q", d.Id())
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEgressOnlyInternetGatewayDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).ec2conn
|
||||||
|
|
||||||
|
_, err := conn.DeleteEgressOnlyInternetGateway(&ec2.DeleteEgressOnlyInternetGatewayInput{
|
||||||
|
EgressOnlyInternetGatewayId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting egress internet gateway: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSEgressOnlyInternetGateway_basic(t *testing.T) {
|
||||||
|
var igw ec2.EgressOnlyInternetGateway
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSEgressOnlyInternetGatewayDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccAWSEgressOnlyInternetGatewayConfig_basic,
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
testAccCheckAWSEgressOnlyInternetGatewayExists("aws_egress_only_internet_gateway.foo", &igw),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSEgressOnlyInternetGatewayDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_egress_only_internet_gateway" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
describe, err := conn.DescribeEgressOnlyInternetGateways(&ec2.DescribeEgressOnlyInternetGatewaysInput{
|
||||||
|
EgressOnlyInternetGatewayIds: []*string{aws.String(rs.Primary.ID)},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if len(describe.EgressOnlyInternetGateways) != 0 &&
|
||||||
|
*describe.EgressOnlyInternetGateways[0].EgressOnlyInternetGatewayId == rs.Primary.ID {
|
||||||
|
return fmt.Errorf("Egress Only Internet Gateway %q still exists", rs.Primary.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSEgressOnlyInternetGatewayExists(n string, igw *ec2.EgressOnlyInternetGateway) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No Egress Only IGW ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||||
|
resp, err := conn.DescribeEgressOnlyInternetGateways(&ec2.DescribeEgressOnlyInternetGatewaysInput{
|
||||||
|
EgressOnlyInternetGatewayIds: []*string{aws.String(rs.Primary.ID)},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(resp.EgressOnlyInternetGateways) == 0 {
|
||||||
|
return fmt.Errorf("Egress Only IGW not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
*igw = *resp.EgressOnlyInternetGateways[0]
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccAWSEgressOnlyInternetGatewayConfig_basic = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "10.1.0.0/16"
|
||||||
|
assign_generated_ipv6_cidr_block = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_egress_only_internet_gateway" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
}
|
||||||
|
`
|
|
@ -175,6 +175,23 @@ func resourceAwsInstance() *schema.Resource {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ipv6_address_count": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ipv6_addresses": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
ConflictsWith: []string{"ipv6_address_count"},
|
||||||
|
},
|
||||||
|
|
||||||
"tenancy": {
|
"tenancy": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
@ -363,6 +380,23 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
UserData: instanceOpts.UserData64,
|
UserData: instanceOpts.UserData64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ipv6_address_count"); ok {
|
||||||
|
runOpts.Ipv6AddressCount = aws.Int64(int64(v.(int)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ipv6_addresses"); ok {
|
||||||
|
ipv6Addresses := make([]*ec2.InstanceIpv6Address, len(v.([]interface{})))
|
||||||
|
for _, address := range v.([]interface{}) {
|
||||||
|
ipv6Address := &ec2.InstanceIpv6Address{
|
||||||
|
Ipv6Address: aws.String(address.(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6Addresses = append(ipv6Addresses, ipv6Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
runOpts.Ipv6Addresses = ipv6Addresses
|
||||||
|
}
|
||||||
|
|
||||||
// Create the instance
|
// Create the instance
|
||||||
log.Printf("[DEBUG] Run configuration: %s", runOpts)
|
log.Printf("[DEBUG] Run configuration: %s", runOpts)
|
||||||
|
|
||||||
|
@ -501,6 +535,13 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
d.Set("subnet_id", ni.SubnetId)
|
d.Set("subnet_id", ni.SubnetId)
|
||||||
d.Set("network_interface_id", ni.NetworkInterfaceId)
|
d.Set("network_interface_id", ni.NetworkInterfaceId)
|
||||||
d.Set("associate_public_ip_address", ni.Association != nil)
|
d.Set("associate_public_ip_address", ni.Association != nil)
|
||||||
|
d.Set("ipv6_address_count", len(ni.Ipv6Addresses))
|
||||||
|
|
||||||
|
var ipv6Addresses []string
|
||||||
|
for _, address := range ni.Ipv6Addresses {
|
||||||
|
ipv6Addresses = append(ipv6Addresses, *address.Ipv6Address)
|
||||||
|
}
|
||||||
|
d.Set("ipv6_addresses", ipv6Addresses)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -498,6 +498,29 @@ func TestAccAWSInstance_vpc(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccAWSInstance_ipv6_supportAddressCount(t *testing.T) {
|
||||||
|
var v ec2.Instance
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccInstanceConfigIpv6Support,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckInstanceExists(
|
||||||
|
"aws_instance.foo", &v),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo",
|
||||||
|
"ipv6_address_count",
|
||||||
|
"1"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccAWSInstance_multipleRegions(t *testing.T) {
|
func TestAccAWSInstance_multipleRegions(t *testing.T) {
|
||||||
var v ec2.Instance
|
var v ec2.Instance
|
||||||
|
|
||||||
|
@ -1123,6 +1146,37 @@ resource "aws_instance" "foo" {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testAccInstanceConfigIpv6Support = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "10.1.0.0/16"
|
||||||
|
assign_generated_ipv6_cidr_block = true
|
||||||
|
tags {
|
||||||
|
Name = "tf-ipv6-instance-acc-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
cidr_block = "10.1.1.0/24"
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
ipv6_cidr_block = "${cidrsubnet(aws_vpc.foo.ipv6_cidr_block, 8, 1)}"
|
||||||
|
tags {
|
||||||
|
Name = "tf-ipv6-instance-acc-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
# us-west-2
|
||||||
|
ami = "ami-c5eabbf5"
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
subnet_id = "${aws_subnet.foo.id}"
|
||||||
|
|
||||||
|
ipv6_address_count = 1
|
||||||
|
tags {
|
||||||
|
Name = "tf-ipv6-instance-acc-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const testAccInstanceConfigMultipleRegions = `
|
const testAccInstanceConfigMultipleRegions = `
|
||||||
provider "aws" {
|
provider "aws" {
|
||||||
alias = "west"
|
alias = "west"
|
||||||
|
|
|
@ -23,31 +23,48 @@ func resourceAwsSubnet() *schema.Resource {
|
||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"vpc_id": &schema.Schema{
|
"vpc_id": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"cidr_block": &schema.Schema{
|
"cidr_block": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"availability_zone": &schema.Schema{
|
"ipv6_cidr_block": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"availability_zone": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"map_public_ip_on_launch": &schema.Schema{
|
"map_public_ip_on_launch": {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: false,
|
Default: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"assign_ipv6_address_on_creation": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ipv6_cidr_block_association_id": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
"tags": tagsSchema(),
|
"tags": tagsSchema(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -62,6 +79,10 @@ func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
VpcId: aws.String(d.Get("vpc_id").(string)),
|
VpcId: aws.String(d.Get("vpc_id").(string)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ipv6_cidr_block"); ok {
|
||||||
|
createOpts.Ipv6CidrBlock = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
resp, err := conn.CreateSubnet(createOpts)
|
resp, err := conn.CreateSubnet(createOpts)
|
||||||
|
|
||||||
|
@ -119,6 +140,11 @@ func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
d.Set("availability_zone", subnet.AvailabilityZone)
|
d.Set("availability_zone", subnet.AvailabilityZone)
|
||||||
d.Set("cidr_block", subnet.CidrBlock)
|
d.Set("cidr_block", subnet.CidrBlock)
|
||||||
d.Set("map_public_ip_on_launch", subnet.MapPublicIpOnLaunch)
|
d.Set("map_public_ip_on_launch", subnet.MapPublicIpOnLaunch)
|
||||||
|
d.Set("assign_ipv6_address_on_creation", subnet.AssignIpv6AddressOnCreation)
|
||||||
|
if subnet.Ipv6CidrBlockAssociationSet != nil {
|
||||||
|
d.Set("ipv6_cidr_block", subnet.Ipv6CidrBlockAssociationSet[0].Ipv6CidrBlock)
|
||||||
|
d.Set("ipv6_cidr_block_association_id", subnet.Ipv6CidrBlockAssociationSet[0].AssociationId)
|
||||||
|
}
|
||||||
d.Set("tags", tagsToMap(subnet.Tags))
|
d.Set("tags", tagsToMap(subnet.Tags))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -135,6 +161,25 @@ func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
d.SetPartial("tags")
|
d.SetPartial("tags")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.HasChange("assign_ipv6_address_on_creation") {
|
||||||
|
modifyOpts := &ec2.ModifySubnetAttributeInput{
|
||||||
|
SubnetId: aws.String(d.Id()),
|
||||||
|
AssignIpv6AddressOnCreation: &ec2.AttributeBooleanValue{
|
||||||
|
Value: aws.Bool(d.Get("assign_ipv6_address_on_creation").(bool)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Subnet modify attributes: %#v", modifyOpts)
|
||||||
|
|
||||||
|
_, err := conn.ModifySubnetAttribute(modifyOpts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
d.SetPartial("assign_ipv6_address_on_creation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if d.HasChange("map_public_ip_on_launch") {
|
if d.HasChange("map_public_ip_on_launch") {
|
||||||
modifyOpts := &ec2.ModifySubnetAttributeInput{
|
modifyOpts := &ec2.ModifySubnetAttributeInput{
|
||||||
SubnetId: aws.String(d.Id()),
|
SubnetId: aws.String(d.Id()),
|
||||||
|
|
|
@ -32,7 +32,7 @@ func TestAccAWSSubnet_basic(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckSubnetDestroy,
|
CheckDestroy: testAccCheckSubnetDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccSubnetConfig,
|
Config: testAccSubnetConfig,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckSubnetExists(
|
testAccCheckSubnetExists(
|
||||||
|
@ -44,6 +44,55 @@ func TestAccAWSSubnet_basic(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccAWSSubnet_ipv6(t *testing.T) {
|
||||||
|
var v ec2.Subnet
|
||||||
|
|
||||||
|
testCheck := func(*terraform.State) error {
|
||||||
|
if v.Ipv6CidrBlockAssociationSet == nil {
|
||||||
|
return fmt.Errorf("Expected IPV6 CIDR Block Association")
|
||||||
|
}
|
||||||
|
|
||||||
|
if *v.AssignIpv6AddressOnCreation != true {
|
||||||
|
return fmt.Errorf("bad AssignIpv6AddressOnCreation: %t", *v.AssignIpv6AddressOnCreation)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
testCheckUpdated := func(*terraform.State) error {
|
||||||
|
if *v.AssignIpv6AddressOnCreation != false {
|
||||||
|
return fmt.Errorf("bad AssignIpv6AddressOnCreation: %t", *v.AssignIpv6AddressOnCreation)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
IDRefreshName: "aws_subnet.foo",
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckSubnetDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccSubnetConfigIpv6,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckSubnetExists(
|
||||||
|
"aws_subnet.foo", &v),
|
||||||
|
testCheck,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccSubnetConfigIpv6Updated,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckSubnetExists(
|
||||||
|
"aws_subnet.foo", &v),
|
||||||
|
testCheckUpdated,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckSubnetDestroy(s *terraform.State) error {
|
func testAccCheckSubnetDestroy(s *terraform.State) error {
|
||||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||||
|
|
||||||
|
@ -119,3 +168,39 @@ resource "aws_subnet" "foo" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testAccSubnetConfigIpv6 = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "10.10.0.0/16"
|
||||||
|
assign_generated_ipv6_cidr_block = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
cidr_block = "10.10.1.0/24"
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
ipv6_cidr_block = "${cidrsubnet(aws_vpc.foo.ipv6_cidr_block, 8, 1)}"
|
||||||
|
map_public_ip_on_launch = true
|
||||||
|
assign_ipv6_address_on_creation = true
|
||||||
|
tags {
|
||||||
|
Name = "tf-subnet-acc-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const testAccSubnetConfigIpv6Updated = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "10.10.0.0/16"
|
||||||
|
assign_generated_ipv6_cidr_block = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
cidr_block = "10.10.1.0/24"
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
ipv6_cidr_block = "${cidrsubnet(aws_vpc.foo.ipv6_cidr_block, 8, 3)}"
|
||||||
|
map_public_ip_on_launch = true
|
||||||
|
assign_ipv6_address_on_creation = false
|
||||||
|
tags {
|
||||||
|
Name = "tf-subnet-acc-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
@ -55,6 +55,13 @@ func resourceAwsVpc() *schema.Resource {
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"assign_generated_ipv6_cidr_block": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
ForceNew: true,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
"main_route_table_id": {
|
"main_route_table_id": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
@ -80,6 +87,16 @@ func resourceAwsVpc() *schema.Resource {
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ipv6_association_id": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ipv6_cidr_block": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
"tags": tagsSchema(),
|
"tags": tagsSchema(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -91,11 +108,14 @@ func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
if v, ok := d.GetOk("instance_tenancy"); ok {
|
if v, ok := d.GetOk("instance_tenancy"); ok {
|
||||||
instance_tenancy = v.(string)
|
instance_tenancy = v.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the VPC
|
// Create the VPC
|
||||||
createOpts := &ec2.CreateVpcInput{
|
createOpts := &ec2.CreateVpcInput{
|
||||||
CidrBlock: aws.String(d.Get("cidr_block").(string)),
|
CidrBlock: aws.String(d.Get("cidr_block").(string)),
|
||||||
InstanceTenancy: aws.String(instance_tenancy),
|
InstanceTenancy: aws.String(instance_tenancy),
|
||||||
|
AmazonProvidedIpv6CidrBlock: aws.Bool(d.Get("assign_generated_ipv6_cidr_block").(bool)),
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] VPC create config: %#v", *createOpts)
|
log.Printf("[DEBUG] VPC create config: %#v", *createOpts)
|
||||||
vpcResp, err := conn.CreateVpc(createOpts)
|
vpcResp, err := conn.CreateVpc(createOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -154,6 +174,14 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
// Tags
|
// Tags
|
||||||
d.Set("tags", tagsToMap(vpc.Tags))
|
d.Set("tags", tagsToMap(vpc.Tags))
|
||||||
|
|
||||||
|
if vpc.Ipv6CidrBlockAssociationSet != nil {
|
||||||
|
d.Set("assign_generated_ipv6_cidr_block", true)
|
||||||
|
d.Set("ipv6_association_id", vpc.Ipv6CidrBlockAssociationSet[0].AssociationId)
|
||||||
|
d.Set("ipv6_cidr_block", vpc.Ipv6CidrBlockAssociationSet[0].Ipv6CidrBlock)
|
||||||
|
} else {
|
||||||
|
d.Set("assign_generated_ipv6_cidr_block", false)
|
||||||
|
}
|
||||||
|
|
||||||
// Attributes
|
// Attributes
|
||||||
attribute := "enableDnsSupport"
|
attribute := "enableDnsSupport"
|
||||||
DescribeAttrOpts := &ec2.DescribeVpcAttributeInput{
|
DescribeAttrOpts := &ec2.DescribeVpcAttributeInput{
|
||||||
|
|
|
@ -36,6 +36,31 @@ func TestAccAWSVpc_basic(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccAWSVpc_enableIpv6(t *testing.T) {
|
||||||
|
var vpc ec2.Vpc
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckVpcDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccVpcConfigIpv6Enabled,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckVpcExists("aws_vpc.foo", &vpc),
|
||||||
|
testAccCheckVpcCidr(&vpc, "10.1.0.0/16"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_vpc.foo", "cidr_block", "10.1.0.0/16"),
|
||||||
|
resource.TestCheckResourceAttrSet(
|
||||||
|
"aws_vpc.foo", "ipv6_association_id"),
|
||||||
|
resource.TestCheckResourceAttrSet(
|
||||||
|
"aws_vpc.foo", "ipv6_cidr_block"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccAWSVpc_dedicatedTenancy(t *testing.T) {
|
func TestAccAWSVpc_dedicatedTenancy(t *testing.T) {
|
||||||
var vpc ec2.Vpc
|
var vpc ec2.Vpc
|
||||||
|
|
||||||
|
@ -251,6 +276,13 @@ resource "aws_vpc" "foo" {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testAccVpcConfigIpv6Enabled = `
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "10.1.0.0/16"
|
||||||
|
assign_generated_ipv6_cidr_block = true
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const testAccVpcConfigUpdate = `
|
const testAccVpcConfigUpdate = `
|
||||||
resource "aws_vpc" "foo" {
|
resource "aws_vpc" "foo" {
|
||||||
cidr_block = "10.1.0.0/16"
|
cidr_block = "10.1.0.0/16"
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
layout: "aws"
|
||||||
|
page_title: "AWS: aws_egress_only_internet_gateway"
|
||||||
|
sidebar_current: "docs-aws-resource-egress-only-internet-gateway"
|
||||||
|
description: |-
|
||||||
|
Provides a resource to create a VPC Egress Only Internet Gateway.
|
||||||
|
---
|
||||||
|
|
||||||
|
# aws\_egress\_only\_internet\_gateway
|
||||||
|
|
||||||
|
[IPv6 only] Creates an egress-only Internet gateway for your VPC.
|
||||||
|
An egress-only Internet gateway is used to enable outbound communication
|
||||||
|
over IPv6 from instances in your VPC to the Internet, and prevents hosts
|
||||||
|
outside of your VPC from initiating an IPv6 connection with your instance.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "10.1.0.0/16"
|
||||||
|
assign_amazon_ipv6_cidr_block = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_egress_only_internet_gateway" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `vpc_id` - (Required) The VPC ID to create in.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The ID of the Egress Only Internet Gateway.
|
|
@ -77,6 +77,8 @@ instances. See [Shutdown Behavior](https://docs.aws.amazon.com/AWSEC2/latest/Use
|
||||||
* `user_data` - (Optional) The user data to provide when launching the instance.
|
* `user_data` - (Optional) The user data to provide when launching the instance.
|
||||||
* `iam_instance_profile` - (Optional) The IAM Instance Profile to
|
* `iam_instance_profile` - (Optional) The IAM Instance Profile to
|
||||||
launch the instance with.
|
launch the instance with.
|
||||||
|
* `ipv6_address_count`- (Optional) A number of IPv6 addresses to associate with the primary network interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet.
|
||||||
|
* `ipv6_addresses` - (Optional) Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface
|
||||||
* `tags` - (Optional) A mapping of tags to assign to the resource.
|
* `tags` - (Optional) A mapping of tags to assign to the resource.
|
||||||
* `root_block_device` - (Optional) Customize details about the root block
|
* `root_block_device` - (Optional) Customize details about the root block
|
||||||
device of the instance. See [Block Devices](#block-devices) below for details.
|
device of the instance. See [Block Devices](#block-devices) below for details.
|
||||||
|
|
|
@ -29,9 +29,14 @@ The following arguments are supported:
|
||||||
|
|
||||||
* `availability_zone`- (Optional) The AZ for the subnet.
|
* `availability_zone`- (Optional) The AZ for the subnet.
|
||||||
* `cidr_block` - (Required) The CIDR block for the subnet.
|
* `cidr_block` - (Required) The CIDR block for the subnet.
|
||||||
|
* `ipv6_cidr_block` - (Optional) The IPv6 network range for the subnet,
|
||||||
|
in CIDR notation. The subnet size must use a /64 prefix length.
|
||||||
* `map_public_ip_on_launch` - (Optional) Specify true to indicate
|
* `map_public_ip_on_launch` - (Optional) Specify true to indicate
|
||||||
that instances launched into the subnet should be assigned
|
that instances launched into the subnet should be assigned
|
||||||
a public IP address.
|
a public IP address. Default is `false`.
|
||||||
|
* `assign_ipv6_address_on_creation` - (Optional) Specify true to indicate
|
||||||
|
that network interfaces created in the specified subnet should be
|
||||||
|
assigned an IPv6 address. Default is `false`
|
||||||
* `vpc_id` - (Required) The VPC ID.
|
* `vpc_id` - (Required) The VPC ID.
|
||||||
* `tags` - (Optional) A mapping of tags to assign to the resource.
|
* `tags` - (Optional) A mapping of tags to assign to the resource.
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,9 @@ The following arguments are supported:
|
||||||
* `enable_classiclink` - (Optional) A boolean flag to enable/disable ClassicLink
|
* `enable_classiclink` - (Optional) A boolean flag to enable/disable ClassicLink
|
||||||
for the VPC. Only valid in regions and accounts that support EC2 Classic.
|
for the VPC. Only valid in regions and accounts that support EC2 Classic.
|
||||||
See the [ClassicLink documentation][1] for more information. Defaults false.
|
See the [ClassicLink documentation][1] for more information. Defaults false.
|
||||||
|
* `assign_generated_ipv6_cidr_block` - (Optional) Requests an Amazon-provided IPv6 CIDR
|
||||||
|
block with a /56 prefix length for the VPC. You cannot specify the range of IP addresses, or
|
||||||
|
the size of the CIDR block. Default is `false`.
|
||||||
* `tags` - (Optional) A mapping of tags to assign to the resource.
|
* `tags` - (Optional) A mapping of tags to assign to the resource.
|
||||||
|
|
||||||
## Attributes Reference
|
## Attributes Reference
|
||||||
|
@ -62,6 +65,8 @@ The following attributes are exported:
|
||||||
* `default_network_acl_id` - The ID of the network ACL created by default on VPC creation
|
* `default_network_acl_id` - The ID of the network ACL created by default on VPC creation
|
||||||
* `default_security_group_id` - The ID of the security group created by default on VPC creation
|
* `default_security_group_id` - The ID of the security group created by default on VPC creation
|
||||||
* `default_route_table_id` - The ID of the route table created by default on VPC creation
|
* `default_route_table_id` - The ID of the route table created by default on VPC creation
|
||||||
|
* `ipv6_association_id` - The association ID for the IPv6 CIDR block.
|
||||||
|
* `ipv6_cidr_block` - The IPv6 CIDR block.
|
||||||
|
|
||||||
|
|
||||||
[1]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-classiclink.html
|
[1]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-classiclink.html
|
||||||
|
|
|
@ -1209,7 +1209,7 @@
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
<li<%= sidebar_current(/^docs-aws-resource-(default|customer|flow|internet-gateway|main-route|network|route-|security-group|subnet|vpc|vpn)/) %>>
|
<li<%= sidebar_current(/^docs-aws-resource-(default|customer|egress-only-internet-gateway|flow|internet-gateway|main-route|network|route-|security-group|subnet|vpc|vpn)/) %>>
|
||||||
<a href="#">VPC Resources</a>
|
<a href="#">VPC Resources</a>
|
||||||
<ul class="nav nav-visible">
|
<ul class="nav nav-visible">
|
||||||
|
|
||||||
|
@ -1229,6 +1229,10 @@
|
||||||
<a href="/docs/providers/aws/r/default_security_group.html">aws_default_security_group</a>
|
<a href="/docs/providers/aws/r/default_security_group.html">aws_default_security_group</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-aws-resource-egress-only-internet-gateway") %>>
|
||||||
|
<a href="/docs/providers/aws/r/egress_only_internet_gateway.html">aws_egress_only_internet_gateway</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-aws-resource-flow-log") %>>
|
<li<%= sidebar_current("docs-aws-resource-flow-log") %>>
|
||||||
<a href="/docs/providers/aws/r/flow_log.html">aws_flow_log</a>
|
<a href="/docs/providers/aws/r/flow_log.html">aws_flow_log</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in New Issue