github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/builtin/providers/aws/resource_aws_network_acl_rule.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/service/ec2"
    12  	"github.com/hashicorp/terraform/helper/hashcode"
    13  	"github.com/hashicorp/terraform/helper/resource"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsNetworkAclRule() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsNetworkAclRuleCreate,
    20  		Read:   resourceAwsNetworkAclRuleRead,
    21  		Delete: resourceAwsNetworkAclRuleDelete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"network_acl_id": &schema.Schema{
    25  				Type:     schema.TypeString,
    26  				Required: true,
    27  				ForceNew: true,
    28  			},
    29  			"rule_number": &schema.Schema{
    30  				Type:     schema.TypeInt,
    31  				Required: true,
    32  				ForceNew: true,
    33  			},
    34  			"egress": &schema.Schema{
    35  				Type:     schema.TypeBool,
    36  				Optional: true,
    37  				ForceNew: true,
    38  				Default:  false,
    39  			},
    40  			"protocol": &schema.Schema{
    41  				Type:     schema.TypeString,
    42  				Required: true,
    43  				ForceNew: true,
    44  			},
    45  			"rule_action": &schema.Schema{
    46  				Type:     schema.TypeString,
    47  				Required: true,
    48  				ForceNew: true,
    49  			},
    50  			"cidr_block": &schema.Schema{
    51  				Type:     schema.TypeString,
    52  				Required: true,
    53  				ForceNew: true,
    54  			},
    55  			"from_port": &schema.Schema{
    56  				Type:     schema.TypeInt,
    57  				Optional: true,
    58  				ForceNew: true,
    59  			},
    60  			"to_port": &schema.Schema{
    61  				Type:     schema.TypeInt,
    62  				Optional: true,
    63  				ForceNew: true,
    64  			},
    65  			"icmp_type": &schema.Schema{
    66  				Type:     schema.TypeInt,
    67  				Optional: true,
    68  				ForceNew: true,
    69  			},
    70  			"icmp_code": &schema.Schema{
    71  				Type:     schema.TypeInt,
    72  				Optional: true,
    73  				ForceNew: true,
    74  			},
    75  		},
    76  	}
    77  }
    78  
    79  func resourceAwsNetworkAclRuleCreate(d *schema.ResourceData, meta interface{}) error {
    80  	conn := meta.(*AWSClient).ec2conn
    81  
    82  	protocol := d.Get("protocol").(string)
    83  	p, protocolErr := strconv.Atoi(protocol)
    84  	if protocolErr != nil {
    85  		var ok bool
    86  		p, ok = protocolIntegers()[protocol]
    87  		if !ok {
    88  			return fmt.Errorf("Invalid Protocol %s for rule %#v", protocol, d.Get("rule_number").(int))
    89  		}
    90  	}
    91  	log.Printf("[INFO] Transformed Protocol %s into %d", protocol, p)
    92  
    93  	params := &ec2.CreateNetworkAclEntryInput{
    94  		NetworkAclId: aws.String(d.Get("network_acl_id").(string)),
    95  		Egress:       aws.Bool(d.Get("egress").(bool)),
    96  		RuleNumber:   aws.Int64(int64(d.Get("rule_number").(int))),
    97  		Protocol:     aws.String(strconv.Itoa(p)),
    98  		CidrBlock:    aws.String(d.Get("cidr_block").(string)),
    99  		RuleAction:   aws.String(d.Get("rule_action").(string)),
   100  		PortRange: &ec2.PortRange{
   101  			From: aws.Int64(int64(d.Get("from_port").(int))),
   102  			To:   aws.Int64(int64(d.Get("to_port").(int))),
   103  		},
   104  	}
   105  
   106  	// Specify additional required fields for ICMP
   107  	if p == 1 {
   108  		params.IcmpTypeCode = &ec2.IcmpTypeCode{}
   109  		if v, ok := d.GetOk("icmp_code"); ok {
   110  			params.IcmpTypeCode.Code = aws.Int64(int64(v.(int)))
   111  		}
   112  		if v, ok := d.GetOk("icmp_type"); ok {
   113  			params.IcmpTypeCode.Type = aws.Int64(int64(v.(int)))
   114  		}
   115  	}
   116  
   117  	log.Printf("[INFO] Creating Network Acl Rule: %d (%t)", d.Get("rule_number").(int), d.Get("egress").(bool))
   118  	_, err := conn.CreateNetworkAclEntry(params)
   119  	if err != nil {
   120  		return fmt.Errorf("Error Creating Network Acl Rule: %s", err.Error())
   121  	}
   122  	d.SetId(networkAclIdRuleNumberEgressHash(d.Get("network_acl_id").(string), d.Get("rule_number").(int), d.Get("egress").(bool), d.Get("protocol").(string)))
   123  
   124  	// It appears it might be a while until the newly created rule is visible via the
   125  	// API (see issue GH-4721). Retry the `findNetworkAclRule` function until it is
   126  	// visible (which in most cases is likely immediately).
   127  	err = resource.Retry(3*time.Minute, func() *resource.RetryError {
   128  		_, findErr := findNetworkAclRule(d, meta)
   129  		if findErr != nil {
   130  			return resource.RetryableError(findErr)
   131  		}
   132  
   133  		return nil
   134  	})
   135  	if err != nil {
   136  		return fmt.Errorf("Created Network ACL Rule was not visible in API within 3 minute period. Running 'terraform apply' again will resume infrastructure creation.")
   137  	}
   138  
   139  	return resourceAwsNetworkAclRuleRead(d, meta)
   140  }
   141  
   142  func resourceAwsNetworkAclRuleRead(d *schema.ResourceData, meta interface{}) error {
   143  	resp, err := findNetworkAclRule(d, meta)
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	d.Set("rule_number", resp.RuleNumber)
   149  	d.Set("cidr_block", resp.CidrBlock)
   150  	d.Set("egress", resp.Egress)
   151  	if resp.IcmpTypeCode != nil {
   152  		d.Set("icmp_code", resp.IcmpTypeCode.Code)
   153  		d.Set("icmp_type", resp.IcmpTypeCode.Type)
   154  	}
   155  	if resp.PortRange != nil {
   156  		d.Set("from_port", resp.PortRange.From)
   157  		d.Set("to_port", resp.PortRange.To)
   158  	}
   159  
   160  	d.Set("rule_action", resp.RuleAction)
   161  
   162  	p, protocolErr := strconv.Atoi(*resp.Protocol)
   163  	log.Printf("[INFO] Converting the protocol %v", p)
   164  	if protocolErr == nil {
   165  		var ok bool
   166  		protocol, ok := protocolStrings(protocolIntegers())[p]
   167  		if !ok {
   168  			return fmt.Errorf("Invalid Protocol %s for rule %#v", *resp.Protocol, d.Get("rule_number").(int))
   169  		}
   170  		log.Printf("[INFO] Transformed Protocol %s back into %s", *resp.Protocol, protocol)
   171  		d.Set("protocol", protocol)
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  func resourceAwsNetworkAclRuleDelete(d *schema.ResourceData, meta interface{}) error {
   178  	conn := meta.(*AWSClient).ec2conn
   179  
   180  	params := &ec2.DeleteNetworkAclEntryInput{
   181  		NetworkAclId: aws.String(d.Get("network_acl_id").(string)),
   182  		RuleNumber:   aws.Int64(int64(d.Get("rule_number").(int))),
   183  		Egress:       aws.Bool(d.Get("egress").(bool)),
   184  	}
   185  
   186  	log.Printf("[INFO] Deleting Network Acl Rule: %s", d.Id())
   187  	_, err := conn.DeleteNetworkAclEntry(params)
   188  	if err != nil {
   189  		return fmt.Errorf("Error Deleting Network Acl Rule: %s", err.Error())
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  func findNetworkAclRule(d *schema.ResourceData, meta interface{}) (*ec2.NetworkAclEntry, error) {
   196  	conn := meta.(*AWSClient).ec2conn
   197  
   198  	filters := make([]*ec2.Filter, 0, 2)
   199  	ruleNumberFilter := &ec2.Filter{
   200  		Name:   aws.String("entry.rule-number"),
   201  		Values: []*string{aws.String(fmt.Sprintf("%v", d.Get("rule_number").(int)))},
   202  	}
   203  	filters = append(filters, ruleNumberFilter)
   204  	egressFilter := &ec2.Filter{
   205  		Name:   aws.String("entry.egress"),
   206  		Values: []*string{aws.String(fmt.Sprintf("%v", d.Get("egress").(bool)))},
   207  	}
   208  	filters = append(filters, egressFilter)
   209  	params := &ec2.DescribeNetworkAclsInput{
   210  		NetworkAclIds: []*string{aws.String(d.Get("network_acl_id").(string))},
   211  		Filters:       filters,
   212  	}
   213  
   214  	log.Printf("[INFO] Describing Network Acl: %s", d.Get("network_acl_id").(string))
   215  	log.Printf("[INFO] Describing Network Acl with the Filters %#v", params)
   216  	resp, err := conn.DescribeNetworkAcls(params)
   217  	if err != nil {
   218  		return nil, fmt.Errorf("Error Finding Network Acl Rule %d: %s", d.Get("rule_number").(int), err.Error())
   219  	}
   220  
   221  	if resp == nil || len(resp.NetworkAcls) != 1 || resp.NetworkAcls[0] == nil {
   222  		return nil, fmt.Errorf(
   223  			"Expected to find one Network ACL, got: %#v",
   224  			resp.NetworkAcls)
   225  	}
   226  	networkAcl := resp.NetworkAcls[0]
   227  	if networkAcl.Entries != nil {
   228  		for _, i := range networkAcl.Entries {
   229  			if *i.RuleNumber == int64(d.Get("rule_number").(int)) && *i.Egress == d.Get("egress").(bool) {
   230  				return i, nil
   231  			}
   232  		}
   233  	}
   234  	return nil, fmt.Errorf(
   235  		"Expected the Network ACL to have Entries, got: %#v",
   236  		networkAcl)
   237  
   238  }
   239  
   240  func networkAclIdRuleNumberEgressHash(networkAclId string, ruleNumber int, egress bool, protocol string) string {
   241  	var buf bytes.Buffer
   242  	buf.WriteString(fmt.Sprintf("%s-", networkAclId))
   243  	buf.WriteString(fmt.Sprintf("%d-", ruleNumber))
   244  	buf.WriteString(fmt.Sprintf("%t-", egress))
   245  	buf.WriteString(fmt.Sprintf("%s-", protocol))
   246  	return fmt.Sprintf("nacl-%d", hashcode.String(buf.String()))
   247  }