provider/aws: Default Network ACL resource (#6165)

* provider/aws: Default Network ACL resource

Provides a resource to manage the default AWS Network ACL. VPC Only.

* Remove subnet_id update, mark as computed value. Remove extra tag update

* refactor default rule number to be a constant

* refactor revokeRulesForType to be revokeAllNetworkACLEntries

Refactor method to delete all network ACL entries, regardless of type. The
previous implementation was under the assumption that we may only eliminate some
rule types and possibly not others, so the split was necessary.

We're now removing them all, so the logic isn't necessary

Several doc and test cleanups are here as well

* smite subnet_id, improve docs
This commit is contained in:
Clint 2016-04-18 11:02:00 -05:00
parent a810edd7a6
commit fcdcb4b916
6 changed files with 896 additions and 5 deletions

View File

@ -199,6 +199,7 @@ func Provider() terraform.ResourceProvider {
"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),
"aws_nat_gateway": resourceAwsNatGateway(),
"aws_network_acl": resourceAwsNetworkAcl(),
"aws_default_network_acl": resourceAwsDefaultNetworkAcl(),
"aws_network_acl_rule": resourceAwsNetworkAclRule(),
"aws_network_interface": resourceAwsNetworkInterface(),
"aws_opsworks_application": resourceAwsOpsworksApplication(),

View File

@ -0,0 +1,283 @@
package aws
import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/schema"
)
// ACL Network ACLs all contain an explicit deny-all rule that cannot be
// destroyed or changed by users. This rule is numbered very high to be a
// catch-all.
// See http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#default-network-acl
const awsDefaultAclRuleNumber = 32767
func resourceAwsDefaultNetworkAcl() *schema.Resource {
return &schema.Resource{
Create: resourceAwsDefaultNetworkAclCreate,
// We reuse aws_network_acl's read method, the operations are the same
Read: resourceAwsNetworkAclRead,
Delete: resourceAwsDefaultNetworkAclDelete,
Update: resourceAwsDefaultNetworkAclUpdate,
Schema: map[string]*schema.Schema{
"vpc_id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"default_network_acl_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Computed: false,
},
// We want explicit management of Subnets here, so we do not allow them to be
// computed. Instead, an empty config will enforce just that; removal of the
// any Subnets that have been assigned to the Default Network ACL. Because we
// can't actually remove them, this will be a continual plan until the
// Subnets are themselves destroyed or reassigned to a different Network
// ACL
"subnet_ids": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
// We want explicit management of Rules here, so we do not allow them to be
// computed. Instead, an empty config will enforce just that; removal of the
// rules
"ingress": &schema.Schema{
Type: schema.TypeSet,
Required: false,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"from_port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"to_port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"rule_no": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"action": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"cidr_block": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"icmp_type": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"icmp_code": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
},
},
Set: resourceAwsNetworkAclEntryHash,
},
"egress": &schema.Schema{
Type: schema.TypeSet,
Required: false,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"from_port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"to_port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"rule_no": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"action": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"cidr_block": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"icmp_type": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"icmp_code": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
},
},
Set: resourceAwsNetworkAclEntryHash,
},
"tags": tagsSchema(),
},
}
}
func resourceAwsDefaultNetworkAclCreate(d *schema.ResourceData, meta interface{}) error {
d.SetId(d.Get("default_network_acl_id").(string))
// revoke all default and pre-existing rules on the default network acl.
// In the UPDATE method, we'll apply only the rules in the configuration.
log.Printf("[DEBUG] Revoking default ingress and egress rules for Default Network ACL for %s", d.Id())
err := revokeAllNetworkACLEntries(d.Id(), meta)
if err != nil {
return err
}
return resourceAwsDefaultNetworkAclUpdate(d, meta)
}
func resourceAwsDefaultNetworkAclUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn
d.Partial(true)
if d.HasChange("ingress") {
err := updateNetworkAclEntries(d, "ingress", conn)
if err != nil {
return err
}
}
if d.HasChange("egress") {
err := updateNetworkAclEntries(d, "egress", conn)
if err != nil {
return err
}
}
if d.HasChange("subnet_ids") {
o, n := d.GetChange("subnet_ids")
if o == nil {
o = new(schema.Set)
}
if n == nil {
n = new(schema.Set)
}
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove := os.Difference(ns).List()
add := ns.Difference(os).List()
if len(remove) > 0 {
//
// NO-OP
//
// Subnets *must* belong to a Network ACL. Subnets are not "removed" from
// Network ACLs, instead their association is replaced. In a normal
// Network ACL, any removal of a Subnet is done by replacing the
// Subnet/ACL association with an association between the Subnet and the
// Default Network ACL. Because we're managing the default here, we cannot
// do that, so we simply log a NO-OP. In order to remove the Subnet here,
// it must be destroyed, or assigned to different Network ACL. Those
// operations are not handled here
log.Printf("[WARN] Cannot remove subnets from the Default Network ACL. They must be re-assigned or destroyed")
}
if len(add) > 0 {
for _, a := range add {
association, err := findNetworkAclAssociation(a.(string), conn)
if err != nil {
return fmt.Errorf("Failed to find acl association: acl %s with subnet %s: %s", d.Id(), a, err)
}
log.Printf("[DEBUG] Updating Network Association for Default Network ACL (%s) and Subnet (%s)", d.Id(), a.(string))
_, err = conn.ReplaceNetworkAclAssociation(&ec2.ReplaceNetworkAclAssociationInput{
AssociationId: association.NetworkAclAssociationId,
NetworkAclId: aws.String(d.Id()),
})
if err != nil {
return err
}
}
}
}
if err := setTags(conn, d); err != nil {
return err
} else {
d.SetPartial("tags")
}
d.Partial(false)
// Re-use the exiting Network ACL Resources READ method
return resourceAwsNetworkAclRead(d, meta)
}
func resourceAwsDefaultNetworkAclDelete(d *schema.ResourceData, meta interface{}) error {
log.Printf("[WARN] Cannot destroy Default Network ACL. Terraform will remove this resource from the state file, however resources may remain.")
d.SetId("")
return nil
}
// revokeAllNetworkACLEntries revoke all ingress and egress rules that the Default
// Network ACL currently has
func revokeAllNetworkACLEntries(netaclId string, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn
resp, err := conn.DescribeNetworkAcls(&ec2.DescribeNetworkAclsInput{
NetworkAclIds: []*string{aws.String(netaclId)},
})
if err != nil {
log.Printf("[DEBUG] Error looking up Network ACL: %s", err)
return err
}
if resp == nil {
return fmt.Errorf("[ERR] Error looking up Default Network ACL Entries: No results")
}
networkAcl := resp.NetworkAcls[0]
for _, e := range networkAcl.Entries {
// Skip the default rules added by AWS. They can be neither
// configured or deleted by users. See http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#default-network-acl
if *e.RuleNumber == awsDefaultAclRuleNumber {
continue
}
// track if this is an egress or ingress rule, for logging purposes
rt := "ingress"
if *e.Egress == true {
rt = "egress"
}
log.Printf("[DEBUG] Destroying Network ACL (%s) Entry number (%d)", rt, int(*e.RuleNumber))
_, err := conn.DeleteNetworkAclEntry(&ec2.DeleteNetworkAclEntryInput{
NetworkAclId: aws.String(netaclId),
RuleNumber: e.RuleNumber,
Egress: e.Egress,
})
if err != nil {
return fmt.Errorf("Error deleting entry (%s): %s", e, err)
}
}
return nil
}

View File

@ -0,0 +1,428 @@
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"
)
var defaultEgressAcl = &ec2.NetworkAclEntry{
CidrBlock: aws.String("0.0.0.0/0"),
Egress: aws.Bool(true),
Protocol: aws.String("-1"),
RuleAction: aws.String("allow"),
RuleNumber: aws.Int64(100),
}
var defaultIngressAcl = &ec2.NetworkAclEntry{
CidrBlock: aws.String("0.0.0.0/0"),
Egress: aws.Bool(false),
Protocol: aws.String("-1"),
RuleAction: aws.String("allow"),
RuleNumber: aws.Int64(100),
}
func TestAccAWSDefaultNetworkAcl_basic(t *testing.T) {
var networkAcl ec2.NetworkAcl
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDefaultNetworkAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDefaultNetworkConfig_basic,
Check: resource.ComposeTestCheckFunc(
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0),
),
},
},
})
}
func TestAccAWSDefaultNetworkAcl_deny_ingress(t *testing.T) {
// TestAccAWSDefaultNetworkAcl_deny_ingress will deny all Ingress rules, but
// not Egress. We then expect there to be 3 rules, 2 AWS defaults and 1
// additional Egress.
var networkAcl ec2.NetworkAcl
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDefaultNetworkAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDefaultNetworkConfig_deny_ingress,
Check: resource.ComposeTestCheckFunc(
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{defaultEgressAcl}, 0),
),
},
},
})
}
func TestAccAWSDefaultNetworkAcl_SubnetRemoval(t *testing.T) {
var networkAcl ec2.NetworkAcl
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDefaultNetworkAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDefaultNetworkConfig_Subnets,
Check: resource.ComposeTestCheckFunc(
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2),
),
},
// Here the Subnets have been removed from the Default Network ACL Config,
// but have not been reassigned. The result is that the Subnets are still
// there, and we have a non-empty plan
resource.TestStep{
Config: testAccAWSDefaultNetworkConfig_Subnets_remove,
Check: resource.ComposeTestCheckFunc(
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2),
),
ExpectNonEmptyPlan: true,
},
},
})
}
func TestAccAWSDefaultNetworkAcl_SubnetReassign(t *testing.T) {
var networkAcl ec2.NetworkAcl
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDefaultNetworkAclDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSDefaultNetworkConfig_Subnets,
Check: resource.ComposeTestCheckFunc(
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2),
),
},
// Here we've reassigned the subnets to a different ACL.
// Without any otherwise association between the `aws_network_acl` and
// `aws_default_network_acl` resources, we cannot guarantee that the
// reassignment of the two subnets to the `aws_network_acl` will happen
// before the update/read on the `aws_default_network_acl` resource.
// Because of this, there could be a non-empty plan if a READ is done on
// the default before the reassignment occurs on the other resource.
//
// For the sake of testing, here we introduce a depends_on attribute from
// the default resource to the other acl resource, to ensure the latter's
// update occurs first, and the former's READ will correctly read zero
// subnets
resource.TestStep{
Config: testAccAWSDefaultNetworkConfig_Subnets_move,
Check: resource.ComposeTestCheckFunc(
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0),
),
},
},
})
}
func testAccCheckAWSDefaultNetworkAclDestroy(s *terraform.State) error {
// We can't destroy this resource; it comes and goes with the VPC itself.
return nil
}
func testAccCheckAWSDefaultACLAttributes(acl *ec2.NetworkAcl, rules []*ec2.NetworkAclEntry, subnetCount int) resource.TestCheckFunc {
return func(s *terraform.State) error {
aclEntriesCount := len(acl.Entries)
ruleCount := len(rules)
// Default ACL has 2 hidden rules we can't do anything about
ruleCount = ruleCount + 2
if ruleCount != aclEntriesCount {
return fmt.Errorf("Expected (%d) Rules, got (%d)", ruleCount, aclEntriesCount)
}
if len(acl.Associations) != subnetCount {
return fmt.Errorf("Expected (%d) Subnets, got (%d)", subnetCount, len(acl.Associations))
}
return nil
}
}
func testAccGetWSDefaultNetworkAcl(n string, networkAcl *ec2.NetworkAcl) 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 Network ACL is set")
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.DescribeNetworkAcls(&ec2.DescribeNetworkAclsInput{
NetworkAclIds: []*string{aws.String(rs.Primary.ID)},
})
if err != nil {
return err
}
if len(resp.NetworkAcls) > 0 && *resp.NetworkAcls[0].NetworkAclId == rs.Primary.ID {
*networkAcl = *resp.NetworkAcls[0]
return nil
}
return fmt.Errorf("Network Acls not found")
}
}
const testAccAWSDefaultNetworkConfig_basic = `
resource "aws_vpc" "tftestvpc" {
cidr_block = "10.1.0.0/16"
tags {
Name = "TestAccAWSDefaultNetworkAcl_basic"
}
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.tftestvpc.default_network_acl_id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_basic"
}
}
`
const testAccAWSDefaultNetworkConfig_basicDefaultRules = `
resource "aws_vpc" "tftestvpc" {
cidr_block = "10.1.0.0/16"
tags {
Name = "TestAccAWSDefaultNetworkAcl_basic"
}
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.tftestvpc.default_network_acl_id}"
ingress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
egress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
tags {
Name = "TestAccAWSDefaultNetworkAcl_basic"
}
}
`
const testAccAWSDefaultNetworkConfig_deny = `
resource "aws_vpc" "tftestvpc" {
cidr_block = "10.1.0.0/16"
tags {
Name = "TestAccAWSDefaultNetworkAcl_basic"
}
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.tftestvpc.default_network_acl_id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_basic"
}
}
`
const testAccAWSDefaultNetworkConfig_deny_ingress = `
resource "aws_vpc" "tftestvpc" {
cidr_block = "10.1.0.0/16"
tags {
Name = "TestAccAWSDefaultNetworkAcl_basic"
}
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.tftestvpc.default_network_acl_id}"
egress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
tags {
Name = "TestAccAWSDefaultNetworkAcl_basic"
}
}
`
const testAccAWSDefaultNetworkConfig_Subnets = `
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_subnet" "one" {
cidr_block = "10.1.111.0/24"
vpc_id = "${aws_vpc.foo.id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_subnet" "two" {
cidr_block = "10.1.1.0/24"
vpc_id = "${aws_vpc.foo.id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_network_acl" "bar" {
vpc_id = "${aws_vpc.foo.id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.foo.default_network_acl_id}"
subnet_ids = ["${aws_subnet.one.id}", "${aws_subnet.two.id}"]
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
`
const testAccAWSDefaultNetworkConfig_Subnets_remove = `
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_subnet" "one" {
cidr_block = "10.1.111.0/24"
vpc_id = "${aws_vpc.foo.id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_subnet" "two" {
cidr_block = "10.1.1.0/24"
vpc_id = "${aws_vpc.foo.id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_network_acl" "bar" {
vpc_id = "${aws_vpc.foo.id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.foo.default_network_acl_id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
`
const testAccAWSDefaultNetworkConfig_Subnets_move = `
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_subnet" "one" {
cidr_block = "10.1.111.0/24"
vpc_id = "${aws_vpc.foo.id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_subnet" "two" {
cidr_block = "10.1.1.0/24"
vpc_id = "${aws_vpc.foo.id}"
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_network_acl" "bar" {
vpc_id = "${aws_vpc.foo.id}"
subnet_ids = ["${aws_subnet.one.id}", "${aws_subnet.two.id}"]
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.foo.default_network_acl_id}"
depends_on = ["aws_network_acl.bar"]
tags {
Name = "TestAccAWSDefaultNetworkAcl_SubnetRemoval"
}
}
`

View File

@ -190,7 +190,7 @@ func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error {
for _, e := range networkAcl.Entries {
// Skip the default rules added by AWS. They can be neither
// configured or deleted by users.
if *e.RuleNumber == 32767 {
if *e.RuleNumber == awsDefaultAclRuleNumber {
continue
}
@ -324,7 +324,6 @@ func resourceAwsNetworkAclUpdate(d *schema.ResourceData, meta interface{}) error
}
func updateNetworkAclEntries(d *schema.ResourceData, entryType string, conn *ec2.EC2) error {
if d.HasChange(entryType) {
o, n := d.GetChange(entryType)
@ -343,16 +342,16 @@ func updateNetworkAclEntries(d *schema.ResourceData, entryType string, conn *ec2
return err
}
for _, remove := range toBeDeleted {
// AWS includes default rules with all network ACLs that can be
// neither modified nor destroyed. They have a custom rule
// number that is out of bounds for any other rule. If we
// encounter it, just continue. There's no work to be done.
if *remove.RuleNumber == 32767 {
if *remove.RuleNumber == awsDefaultAclRuleNumber {
continue
}
// Delete old Acl
log.Printf("[DEBUG] Destroying Network ACL Entry number (%d)", int(*remove.RuleNumber))
_, err := conn.DeleteNetworkAclEntry(&ec2.DeleteNetworkAclEntryInput{
NetworkAclId: aws.String(d.Id()),
RuleNumber: remove.RuleNumber,

View File

@ -0,0 +1,176 @@
---
layout: "aws"
page_title: "AWS: aws_default_network_acl"
sidebar_current: "docs-aws-resource-default-network-acl"
description: |-
Manage the default Network ACL resource.
---
# aws\_default\_network\_acl
Provides a resource to manage the default AWS Network ACL. VPC Only.
Each VPC created in AWS comes with a Default Network ACL that can be managed, but not
destroyed. **This is an advanced resource**, and has special caveats to be aware
of when using it. Please read this document in its entirety before using this
resource.
The `aws_default_network_acl` behaves differently from normal resources, in that
Terraform does not _create_ this resource, but instead attempts to "adopt" it
into management. We can do this because each VPC created has a Default Network
ACL that cannot be destroyed, and is created with a known set of default rules.
When Terraform first adopts the Default Network ACL, it **immediately removes all
rules in the ACL**. It then proceeds to create any rules specified in the
configuration. This step is required so that only the rules specified in the
configuration are created.
For more information about Network ACLs, see the AWS Documentation on
[Network ACLs][aws-network-acls].
## Basic Example Usage, with default rules
The following config gives the Default Network ACL the same rules that AWS
includes, but pulls the resource under management by Terraform. This means that
any ACL rules added or changed will be detected as drift.
```
resource "aws_vpc" "mainvpc" {
cidr_block = "10.1.0.0/16"
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.mainvpc.default_network_acl_id}"
ingress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
egress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
}
```
## Example config to deny all Egress traffic, allowing Ingress
The following denies all Egress traffic by omitting any `egress` rules, while
including the default `ingress` rule to allow all traffic.
```
resource "aws_vpc" "mainvpc" {
cidr_block = "10.1.0.0/16"
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.mainvpc.default_network_acl_id}"
ingress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
}
```
## Example config to deny all traffic to any Subnet in the Default Network ACL:
This config denies all traffic in the Default ACL. This can be useful if you
want a locked down default to force all resources in the VPC to assign a
non-default ACL.
```
resource "aws_vpc" "mainvpc" {
cidr_block = "10.1.0.0/16"
}
resource "aws_default_network_acl" "default" {
default_network_acl_id = "${aws_vpc.mainvpc.default_network_acl_id}"
# no rules defined, deny all traffic in this ACL
}
```
## Argument Reference
The following arguments are supported:
* `default_network_acl_id` - (Required) The Network ACL ID to manage. This
attribute is exported from `aws_vpc`, or manually found via the AWS Console.
* `subnet_ids` - (Optional) A list of Subnet IDs to apply the ACL to. See the
notes below on managing Subnets in the Default VPC
* `ingress` - (Optional) Specifies an ingress rule. Parameters defined below.
* `egress` - (Optional) Specifies an egress rule. Parameters defined below.
* `tags` - (Optional) A mapping of tags to assign to the resource.
Both `egress` and `ingress` support the following keys:
* `from_port` - (Required) The from port to match.
* `to_port` - (Required) The to port to match.
* `rule_no` - (Required) The rule number. Used for ordering.
* `action` - (Required) The action to take.
* `protocol` - (Required) The protocol to match. If using the -1 'all'
protocol, you must specify a from and to port of 0.
* `cidr_block` - (Optional) The CIDR block to match. This must be a
valid network mask.
* `icmp_type` - (Optional) The ICMP type to be used. Default 0.
* `icmp_code` - (Optional) The ICMP type code to be used. Default 0.
~> Note: For more information on ICMP types and codes, see here: http://www.nthelp.com/icmp.html
### Managing Subnets in the Default Network ACL
Within a VPC, all Subnets must be associated with a Network ACL. In order to
"delete" the association between a Subnet and a non-default Network ACL, the
association is destroyed by replacing it with an association between the Subnet
and the Default ACL instead.
When managing the Default Network ACL, you cannot "remove" Subnets.
Instead, they must be reassigned to another Network ACL, or the Subnet itself must be
destroyed. Because of these requirements, removing the `subnet_ids` attribute from the
configuration of a `aws_default_network_acl` resource may result in a reoccurring
plan, until the Subnets are reassigned to another Network ACL or are destroyed.
Because Subnets are by default associated with the Default Network ACL, any
non-explicit association will show up as a plan to remove the Subnet. For
example: if you have a custom `aws_network_acl` with two subnets attached, and
you remove the `aws_network_acl` resource, after successfully destroying this
resource future plans will show a diff on the managed `aws_default_network_acl`,
as those two Subnets have been orphaned by the now destroyed network acl and thus
adopted by the Default Network ACL. In order to avoid a reoccurring plan, they
will need to be reassigned, destroyed, or added to the `subnet_ids` attribute of
the `aws_default_network_acl` entry.
### Removing `aws_default_network_acl` from your configuration
Each AWS VPC comes with a Default Network ACL that cannot be deleted. The `aws_default_network_acl`
allows you to manage this Network ACL, but Terraform cannot destroy it. Removing
this resource from your configuration will remove it from your statefile and
management, **but will not destroy the Network ACL.** All Subnets associations
and ingress or egress rules will be left as they are at the time of removal. You
can resume managing them via the AWS Console.
## Attributes Reference
The following attributes are exported:
* `id` - The ID of the Default Network ACL
* `vpc_id` - The ID of the associated VPC
* `ingress` - Set of ingress rules
* `egress` - Set of egress rules
* `subnet_ids`  IDs of associated Subnets
[aws-network-acls]: http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html

View File

@ -652,7 +652,7 @@
</li>
<li<%= sidebar_current(/^docs-aws-resource-(customer|flow|internet-gateway|main-route|network|route-|security-group|subnet|vpc|vpn)/) %>>
<li<%= sidebar_current(/^docs-aws-resource-(default|customer|flow|internet-gateway|main-route|network|route-|security-group|subnet|vpc|vpn)/) %>>
<a href="#">VPC Resources</a>
<ul class="nav nav-visible">
@ -676,6 +676,10 @@
<a href="/docs/providers/aws/r/nat_gateway.html">aws_nat_gateway</a>
</li>
<li<%= sidebar_current("docs-aws-resource-default-network-acl") %>>
<a href="/docs/providers/aws/r/default_network_acl.html">aws_default_network_acl</a>
</li>
<li<%= sidebar_current("docs-aws-resource-network-acl") %>>
<a href="/docs/providers/aws/r/network_acl.html">aws_network_acl</a>
</li>