mirror of https://github.com/hashicorp/terraform
parent
a3cc4a2670
commit
2df8d7d9b0
@ -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 (%s)", 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,110 @@
|
||||
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 {
|
||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "aws_network_acl_rule" {
|
||||
continue
|
||||
}
|
||||
|
||||
rule_number := rs.Primary.Attributes["rule_number"].(int)
|
||||
egress := rs.Primary.Attributes["egress"].(bool)
|
||||
|
||||
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 {
|
||||
for _, i := range networkAcl.Entries {
|
||||
if *i.RuleNumber == int64(rule_number) && *i.Egress == egress {
|
||||
return fmt.Errorf("Network ACL Rule (%s) still exists.", rs.Primary.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ec2err, ok := err.(awserr.Error)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
// Confirm error code is what we want
|
||||
if ec2err.Code() != "InvalidNetworkAclEntry.NotFound" {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckAWSNetworkAclRuleExists(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 Security Group is set")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
`
|
||||
Loading…
Reference in new issue