diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 01d55a8992d9..f8606d5ab639 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -197,6 +197,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_stack": resourceAwsOpsworksStack(), diff --git a/builtin/providers/aws/resource_aws_default_network_acl.go b/builtin/providers/aws/resource_aws_default_network_acl.go new file mode 100644 index 000000000000..44443e924268 --- /dev/null +++ b/builtin/providers/aws/resource_aws_default_network_acl.go @@ -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 +} diff --git a/builtin/providers/aws/resource_aws_default_network_acl_test.go b/builtin/providers/aws/resource_aws_default_network_acl_test.go new file mode 100644 index 000000000000..628943634ba0 --- /dev/null +++ b/builtin/providers/aws/resource_aws_default_network_acl_test.go @@ -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" + } +} +` diff --git a/builtin/providers/aws/resource_aws_network_acl.go b/builtin/providers/aws/resource_aws_network_acl.go index b8fe88021544..1a11e4197681 100644 --- a/builtin/providers/aws/resource_aws_network_acl.go +++ b/builtin/providers/aws/resource_aws_network_acl.go @@ -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, diff --git a/website/source/docs/providers/aws/r/default_network_acl.html.markdown b/website/source/docs/providers/aws/r/default_network_acl.html.markdown new file mode 100644 index 000000000000..e50a786b79bb --- /dev/null +++ b/website/source/docs/providers/aws/r/default_network_acl.html.markdown @@ -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 diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index aba5778c92f1..d7d7f27f0b42 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -632,7 +632,7 @@ -