Merge pull request #4286 from stack72/aws-network-acl-rule
provider/aws: aws_network_acl_rule
This commit is contained in:
commit
7c1811df36
|
@ -69,6 +69,15 @@ func flattenNetworkAclEntries(list []*ec2.NetworkAclEntry) []map[string]interfac
|
|||
|
||||
}
|
||||
|
||||
func protocolStrings(protocolIntegers map[string]int) map[int]string {
|
||||
protocolStrings := make(map[int]string, len(protocolIntegers))
|
||||
for k, v := range protocolIntegers {
|
||||
protocolStrings[v] = k
|
||||
}
|
||||
|
||||
return protocolStrings
|
||||
}
|
||||
|
||||
func protocolIntegers() map[string]int {
|
||||
var protocolIntegers = make(map[string]int)
|
||||
protocolIntegers = map[string]int{
|
||||
|
|
|
@ -153,6 +153,7 @@ func Provider() terraform.ResourceProvider {
|
|||
"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),
|
||||
"aws_nat_gateway": resourceAwsNatGateway(),
|
||||
"aws_network_acl": resourceAwsNetworkAcl(),
|
||||
"aws_network_acl_rule": resourceAwsNetworkAclRule(),
|
||||
"aws_network_interface": resourceAwsNetworkInterface(),
|
||||
"aws_opsworks_stack": resourceAwsOpsworksStack(),
|
||||
"aws_opsworks_java_app_layer": resourceAwsOpsworksJavaAppLayer(),
|
||||
|
|
|
@ -50,6 +50,7 @@ func resourceAwsNetworkAcl() *schema.Resource {
|
|||
Type: schema.TypeSet,
|
||||
Required: false,
|
||||
Optional: true,
|
||||
Computed: false,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"from_port": &schema.Schema{
|
||||
|
@ -92,6 +93,7 @@ func resourceAwsNetworkAcl() *schema.Resource {
|
|||
Type: schema.TypeSet,
|
||||
Required: false,
|
||||
Optional: true,
|
||||
Computed: false,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"from_port": &schema.Schema{
|
||||
|
@ -316,6 +318,7 @@ 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)
|
||||
|
||||
if o == nil {
|
||||
|
@ -399,6 +402,7 @@ func updateNetworkAclEntries(d *schema.ResourceData, entryType string, conn *ec2
|
|||
return fmt.Errorf("Error creating %s entry: %s", entryType, connErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func resourceAwsNetworkAclRule() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceAwsNetworkAclRuleCreate,
|
||||
Read: resourceAwsNetworkAclRuleRead,
|
||||
Delete: resourceAwsNetworkAclRuleDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"network_acl_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"rule_number": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"egress": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: false,
|
||||
},
|
||||
"protocol": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"rule_action": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"cidr_block": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"from_port": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"to_port": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"icmp_type": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"icmp_code": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceAwsNetworkAclRuleCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).ec2conn
|
||||
|
||||
protocol := d.Get("protocol").(string)
|
||||
p, protocolErr := strconv.Atoi(protocol)
|
||||
if protocolErr != nil {
|
||||
var ok bool
|
||||
p, ok = protocolIntegers()[protocol]
|
||||
if !ok {
|
||||
return fmt.Errorf("Invalid Protocol %s for rule %#v", protocol, d.Get("rule_number").(int))
|
||||
}
|
||||
}
|
||||
log.Printf("[INFO] Transformed Protocol %s into %d", protocol, p)
|
||||
|
||||
params := &ec2.CreateNetworkAclEntryInput{
|
||||
NetworkAclId: aws.String(d.Get("network_acl_id").(string)),
|
||||
Egress: aws.Bool(d.Get("egress").(bool)),
|
||||
RuleNumber: aws.Int64(int64(d.Get("rule_number").(int))),
|
||||
Protocol: aws.String(strconv.Itoa(p)),
|
||||
CidrBlock: aws.String(d.Get("cidr_block").(string)),
|
||||
RuleAction: aws.String(d.Get("rule_action").(string)),
|
||||
PortRange: &ec2.PortRange{
|
||||
From: aws.Int64(int64(d.Get("from_port").(int))),
|
||||
To: aws.Int64(int64(d.Get("to_port").(int))),
|
||||
},
|
||||
}
|
||||
|
||||
// Specify additional required fields for ICMP
|
||||
if p == 1 {
|
||||
params.IcmpTypeCode = &ec2.IcmpTypeCode{}
|
||||
if v, ok := d.GetOk("icmp_code"); ok {
|
||||
params.IcmpTypeCode.Code = aws.Int64(int64(v.(int)))
|
||||
}
|
||||
if v, ok := d.GetOk("icmp_type"); ok {
|
||||
params.IcmpTypeCode.Type = aws.Int64(int64(v.(int)))
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Creating Network Acl Rule: %d (%t)", d.Get("rule_number").(int), d.Get("egress").(bool))
|
||||
_, err := conn.CreateNetworkAclEntry(params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error Creating Network Acl Rule: %s", err.Error())
|
||||
}
|
||||
d.SetId(networkAclIdRuleNumberEgressHash(d.Get("network_acl_id").(string), d.Get("rule_number").(int), d.Get("egress").(bool), d.Get("protocol").(string)))
|
||||
return resourceAwsNetworkAclRuleRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceAwsNetworkAclRuleRead(d *schema.ResourceData, meta interface{}) error {
|
||||
resp, err := findNetworkAclRule(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Set("rule_number", resp.RuleNumber)
|
||||
d.Set("cidr_block", resp.CidrBlock)
|
||||
d.Set("egress", resp.Egress)
|
||||
if resp.IcmpTypeCode != nil {
|
||||
d.Set("icmp_code", resp.IcmpTypeCode.Code)
|
||||
d.Set("icmp_type", resp.IcmpTypeCode.Type)
|
||||
}
|
||||
if resp.PortRange != nil {
|
||||
d.Set("from_port", resp.PortRange.From)
|
||||
d.Set("to_port", resp.PortRange.To)
|
||||
}
|
||||
|
||||
d.Set("rule_action", resp.RuleAction)
|
||||
|
||||
p, protocolErr := strconv.Atoi(*resp.Protocol)
|
||||
log.Printf("[INFO] Converting the protocol %v", p)
|
||||
if protocolErr == nil {
|
||||
var ok bool
|
||||
protocol, ok := protocolStrings(protocolIntegers())[p]
|
||||
if !ok {
|
||||
return fmt.Errorf("Invalid Protocol %s for rule %#v", *resp.Protocol, d.Get("rule_number").(int))
|
||||
}
|
||||
log.Printf("[INFO] Transformed Protocol %s back into %s", *resp.Protocol, protocol)
|
||||
d.Set("protocol", protocol)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceAwsNetworkAclRuleDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).ec2conn
|
||||
|
||||
params := &ec2.DeleteNetworkAclEntryInput{
|
||||
NetworkAclId: aws.String(d.Get("network_acl_id").(string)),
|
||||
RuleNumber: aws.Int64(int64(d.Get("rule_number").(int))),
|
||||
Egress: aws.Bool(d.Get("egress").(bool)),
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Deleting Network Acl Rule: %s", d.Id())
|
||||
_, err := conn.DeleteNetworkAclEntry(params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error Deleting Network Acl Rule: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findNetworkAclRule(d *schema.ResourceData, meta interface{}) (*ec2.NetworkAclEntry, error) {
|
||||
conn := meta.(*AWSClient).ec2conn
|
||||
|
||||
filters := make([]*ec2.Filter, 0, 2)
|
||||
ruleNumberFilter := &ec2.Filter{
|
||||
Name: aws.String("entry.rule-number"),
|
||||
Values: []*string{aws.String(fmt.Sprintf("%v", d.Get("rule_number").(int)))},
|
||||
}
|
||||
filters = append(filters, ruleNumberFilter)
|
||||
egressFilter := &ec2.Filter{
|
||||
Name: aws.String("entry.egress"),
|
||||
Values: []*string{aws.String(fmt.Sprintf("%v", d.Get("egress").(bool)))},
|
||||
}
|
||||
filters = append(filters, egressFilter)
|
||||
params := &ec2.DescribeNetworkAclsInput{
|
||||
NetworkAclIds: []*string{aws.String(d.Get("network_acl_id").(string))},
|
||||
Filters: filters,
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Describing Network Acl: %s", d.Get("network_acl_id").(string))
|
||||
log.Printf("[INFO] Describing Network Acl with the Filters %#v", params)
|
||||
resp, err := conn.DescribeNetworkAcls(params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error Finding Network Acl Rule %d: %s", d.Get("rule_number").(int), err.Error())
|
||||
}
|
||||
|
||||
if resp == nil || len(resp.NetworkAcls) != 1 || resp.NetworkAcls[0] == nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Expected to find one Network ACL, got: %#v",
|
||||
resp.NetworkAcls)
|
||||
}
|
||||
networkAcl := resp.NetworkAcls[0]
|
||||
if networkAcl.Entries != nil {
|
||||
for _, i := range networkAcl.Entries {
|
||||
if *i.RuleNumber == int64(d.Get("rule_number").(int)) && *i.Egress == d.Get("egress").(bool) {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf(
|
||||
"Expected the Network ACL to have Entries, got: %#v",
|
||||
networkAcl)
|
||||
|
||||
}
|
||||
|
||||
func networkAclIdRuleNumberEgressHash(networkAclId string, ruleNumber int, egress bool, protocol string) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(fmt.Sprintf("%s-", networkAclId))
|
||||
buf.WriteString(fmt.Sprintf("%d-", ruleNumber))
|
||||
buf.WriteString(fmt.Sprintf("%t-", egress))
|
||||
buf.WriteString(fmt.Sprintf("%s-", protocol))
|
||||
return fmt.Sprintf("nacl-%d", hashcode.String(buf.String()))
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"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/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccAWSNetworkAclRule_basic(t *testing.T) {
|
||||
var networkAcl ec2.NetworkAcl
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSNetworkAclRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSNetworkAclRuleBasicConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSNetworkAclRuleExists("aws_network_acl_rule.bar", &networkAcl),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckAWSNetworkAclRuleDestroy(s *terraform.State) error {
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||
if rs.Type != "aws_network_acl_rule" {
|
||||
continue
|
||||
}
|
||||
|
||||
req := &ec2.DescribeNetworkAclsInput{
|
||||
NetworkAclIds: []*string{aws.String(rs.Primary.ID)},
|
||||
}
|
||||
resp, err := conn.DescribeNetworkAcls(req)
|
||||
if err == nil {
|
||||
if len(resp.NetworkAcls) > 0 && *resp.NetworkAcls[0].NetworkAclId == rs.Primary.ID {
|
||||
networkAcl := resp.NetworkAcls[0]
|
||||
if networkAcl.Entries != nil {
|
||||
return fmt.Errorf("Network ACL Entries still exist")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ec2err, ok := err.(awserr.Error)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
if ec2err.Code() != "InvalidNetworkAclEntry.NotFound" {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckAWSNetworkAclRuleExists(n string, networkAcl *ec2.NetworkAcl) resource.TestCheckFunc {
|
||||
|
||||
return func(s *terraform.State) error {
|
||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||
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 Id is set")
|
||||
}
|
||||
|
||||
req := &ec2.DescribeNetworkAclsInput{
|
||||
NetworkAclIds: []*string{aws.String(rs.Primary.ID)},
|
||||
}
|
||||
resp, err := conn.DescribeNetworkAcls(req)
|
||||
if err == nil {
|
||||
if len(resp.NetworkAcls) > 0 && *resp.NetworkAcls[0].NetworkAclId == rs.Primary.ID {
|
||||
networkAcl := resp.NetworkAcls[0]
|
||||
if networkAcl.Entries == nil {
|
||||
return fmt.Errorf("No Network ACL Entries exist")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ec2err, ok := err.(awserr.Error)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
if ec2err.Code() != "InvalidNetworkAclEntry.NotFound" {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const testAccAWSNetworkAclRuleBasicConfig = `
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
resource "aws_vpc" "foo" {
|
||||
cidr_block = "10.3.0.0/16"
|
||||
}
|
||||
resource "aws_network_acl" "bar" {
|
||||
vpc_id = "${aws_vpc.foo.id}"
|
||||
}
|
||||
resource "aws_network_acl_rule" "bar" {
|
||||
network_acl_id = "${aws_network_acl.bar.id}"
|
||||
rule_number = 200
|
||||
egress = false
|
||||
protocol = "tcp"
|
||||
rule_action = "allow"
|
||||
cidr_block = "0.0.0.0/0"
|
||||
from_port = 22
|
||||
to_port = 22
|
||||
}
|
||||
`
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_network_acl_rule"
|
||||
sidebar_current: "docs-aws-resource-network-acl-rule"
|
||||
description: |-
|
||||
Provides an network ACL Rule resource.
|
||||
---
|
||||
|
||||
# aws\_network\_acl\_rule
|
||||
|
||||
Creates an entry (a rule) in a network ACL with the specified rule number.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
resource "aws_network_acl" "bar" {
|
||||
vpc_id = "${aws_vpc.foo.id}"
|
||||
}
|
||||
resource "aws_network_acl_rule" "bar" {
|
||||
network_acl_id = "${aws_network_acl.bar.id}"
|
||||
rule_number = 200
|
||||
egress = false
|
||||
protocol = "tcp"
|
||||
rule_action = "allow"
|
||||
cidr_block = "0.0.0.0/0"
|
||||
from_port = 22
|
||||
to_port = 22
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `network_acl_id` - (Required) The ID of the network ACL.
|
||||
* `rule_number` - (Required) The rule number for the entry (for example, 100). ACL entries are processed in ascending order by rule number.
|
||||
* `egress` - (Optional, bool) Indicates whether this is an egress rule (rule is applied to traffic leaving the subnet). Default `false`.
|
||||
* `protocol` - (Required) The protocol. A value of -1 means all protocols.
|
||||
* `rule_action` - (Required) Indicates whether to allow or deny the traffic that matches the rule. Accepted values: `allow` | `deny`
|
||||
* `cidr_block` - (Required) The network range to allow or deny, in CIDR notation (for example 172.16.0.0/24 ).
|
||||
* `from_port` - (Optional) The from port to match.
|
||||
* `to_port` - (Optional) The to port to match.
|
||||
* `icmp_type` - (Optional) ICMP protocol: The ICMP type. Required if specifying ICMP for the protocol. e.g. -1
|
||||
* `icmp_code` - (Optional) ICMP protocol: The ICMP code. Required if specifying ICMP for the protocol. e.g. -1
|
||||
|
||||
~> Note: For more information on ICMP types and codes, see here: http://www.nthelp.com/icmp.html
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `id` - The ID of the network ACL Rule
|
||||
|
|
@ -538,6 +538,10 @@
|
|||
<a href="/docs/providers/aws/r/network_acl.html">aws_network_acl</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-aws-resource-network-acl-rule") %>>
|
||||
<a href="/docs/providers/aws/r/network_acl_rule.html">aws_network_acl_rule</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-aws-resource-network-interface") %>>
|
||||
<a href="/docs/providers/aws/r/network_interface.html">aws_network_interface</a>
|
||||
</li>
|
||||
|
|
Loading…
Reference in New Issue