github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/builtin/providers/aws/resource_aws_network_acl.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  	"github.com/mitchellh/goamz/ec2"
    13  )
    14  
    15  func resourceAwsNetworkAcl() *schema.Resource {
    16  
    17  	return &schema.Resource{
    18  		Create: resourceAwsNetworkAclCreate,
    19  		Read:   resourceAwsNetworkAclRead,
    20  		Delete: resourceAwsNetworkAclDelete,
    21  		Update: resourceAwsNetworkAclUpdate,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"vpc_id": &schema.Schema{
    25  				Type:     schema.TypeString,
    26  				Required: true,
    27  				ForceNew: true,
    28  				Computed: false,
    29  			},
    30  			"subnet_id": &schema.Schema{
    31  				Type:     schema.TypeString,
    32  				Optional: true,
    33  				ForceNew: true,
    34  				Computed: false,
    35  			},
    36  			"ingress": &schema.Schema{
    37  				Type:     schema.TypeSet,
    38  				Required: false,
    39  				Optional: true,
    40  				Elem: &schema.Resource{
    41  					Schema: map[string]*schema.Schema{
    42  						"from_port": &schema.Schema{
    43  							Type:     schema.TypeInt,
    44  							Required: true,
    45  						},
    46  						"to_port": &schema.Schema{
    47  							Type:     schema.TypeInt,
    48  							Required: true,
    49  						},
    50  						"rule_no": &schema.Schema{
    51  							Type:     schema.TypeInt,
    52  							Required: true,
    53  						},
    54  						"action": &schema.Schema{
    55  							Type:     schema.TypeString,
    56  							Required: true,
    57  						},
    58  						"protocol": &schema.Schema{
    59  							Type:     schema.TypeString,
    60  							Required: true,
    61  						},
    62  						"cidr_block": &schema.Schema{
    63  							Type:     schema.TypeString,
    64  							Optional: true,
    65  						},
    66  					},
    67  				},
    68  				Set: resourceAwsNetworkAclEntryHash,
    69  			},
    70  			"egress": &schema.Schema{
    71  				Type:     schema.TypeSet,
    72  				Required: false,
    73  				Optional: true,
    74  				Elem: &schema.Resource{
    75  					Schema: map[string]*schema.Schema{
    76  						"from_port": &schema.Schema{
    77  							Type:     schema.TypeInt,
    78  							Required: true,
    79  						},
    80  						"to_port": &schema.Schema{
    81  							Type:     schema.TypeInt,
    82  							Required: true,
    83  						},
    84  						"rule_no": &schema.Schema{
    85  							Type:     schema.TypeInt,
    86  							Required: true,
    87  						},
    88  						"action": &schema.Schema{
    89  							Type:     schema.TypeString,
    90  							Required: true,
    91  						},
    92  						"protocol": &schema.Schema{
    93  							Type:     schema.TypeString,
    94  							Required: true,
    95  						},
    96  						"cidr_block": &schema.Schema{
    97  							Type:     schema.TypeString,
    98  							Optional: true,
    99  						},
   100  					},
   101  				},
   102  				Set: resourceAwsNetworkAclEntryHash,
   103  			},
   104  			"tags": tagsSchema(),
   105  		},
   106  	}
   107  }
   108  
   109  func resourceAwsNetworkAclCreate(d *schema.ResourceData, meta interface{}) error {
   110  
   111  	ec2conn := meta.(*AWSClient).ec2conn
   112  
   113  	// Create the Network Acl
   114  	createOpts := &ec2.CreateNetworkAcl{
   115  		VpcId: d.Get("vpc_id").(string),
   116  	}
   117  
   118  	log.Printf("[DEBUG] Network Acl create config: %#v", createOpts)
   119  	resp, err := ec2conn.CreateNetworkAcl(createOpts)
   120  	if err != nil {
   121  		return fmt.Errorf("Error creating network acl: %s", err)
   122  	}
   123  
   124  	// Get the ID and store it
   125  	networkAcl := &resp.NetworkAcl
   126  	d.SetId(networkAcl.NetworkAclId)
   127  	log.Printf("[INFO] Network Acl ID: %s", networkAcl.NetworkAclId)
   128  
   129  	// Update rules and subnet association once acl is created
   130  	return resourceAwsNetworkAclUpdate(d, meta)
   131  }
   132  
   133  func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error {
   134  	ec2conn := meta.(*AWSClient).ec2conn
   135  
   136  	resp, err := ec2conn.NetworkAcls([]string{d.Id()}, ec2.NewFilter())
   137  
   138  	if err != nil {
   139  		return err
   140  	}
   141  	if resp == nil {
   142  		return nil
   143  	}
   144  
   145  	networkAcl := &resp.NetworkAcls[0]
   146  	var ingressEntries []ec2.NetworkAclEntry
   147  	var egressEntries []ec2.NetworkAclEntry
   148  
   149  	// separate the ingress and egress rules
   150  	for _, e := range networkAcl.EntrySet {
   151  		if e.Egress == true {
   152  			egressEntries = append(egressEntries, e)
   153  		} else {
   154  			ingressEntries = append(ingressEntries, e)
   155  		}
   156  	}
   157  
   158  	d.Set("vpc_id", networkAcl.VpcId)
   159  	d.Set("ingress", ingressEntries)
   160  	d.Set("egress", egressEntries)
   161  	d.Set("tags", tagsToMap(networkAcl.Tags))
   162  
   163  	return nil
   164  }
   165  
   166  func resourceAwsNetworkAclUpdate(d *schema.ResourceData, meta interface{}) error {
   167  	ec2conn := meta.(*AWSClient).ec2conn
   168  	d.Partial(true)
   169  
   170  	if d.HasChange("ingress") {
   171  		err := updateNetworkAclEntries(d, "ingress", ec2conn)
   172  		if err != nil {
   173  			return err
   174  		}
   175  	}
   176  
   177  	if d.HasChange("egress") {
   178  		err := updateNetworkAclEntries(d, "egress", ec2conn)
   179  		if err != nil {
   180  			return err
   181  		}
   182  	}
   183  
   184  	if d.HasChange("subnet_id") {
   185  
   186  		//associate new subnet with the acl.
   187  		_, n := d.GetChange("subnet_id")
   188  		newSubnet := n.(string)
   189  		association, err := findNetworkAclAssociation(newSubnet, ec2conn)
   190  		if err != nil {
   191  			return fmt.Errorf("Failed to update acl %s with subnet %s: %s", d.Id(), newSubnet, err)
   192  		}
   193  		_, err = ec2conn.ReplaceNetworkAclAssociation(association.NetworkAclAssociationId, d.Id())
   194  		if err != nil {
   195  			return err
   196  		}
   197  	}
   198  
   199  	if err := setTags(ec2conn, d); err != nil {
   200  		return err
   201  	} else {
   202  		d.SetPartial("tags")
   203  	}
   204  
   205  	d.Partial(false)
   206  	return resourceAwsNetworkAclRead(d, meta)
   207  }
   208  
   209  func updateNetworkAclEntries(d *schema.ResourceData, entryType string, ec2conn *ec2.EC2) error {
   210  
   211  	o, n := d.GetChange(entryType)
   212  
   213  	if o == nil {
   214  		o = new(schema.Set)
   215  	}
   216  	if n == nil {
   217  		n = new(schema.Set)
   218  	}
   219  
   220  	os := o.(*schema.Set)
   221  	ns := n.(*schema.Set)
   222  
   223  	toBeDeleted, err := expandNetworkAclEntries(os.Difference(ns).List(), entryType)
   224  	if err != nil {
   225  		return err
   226  	}
   227  	for _, remove := range toBeDeleted {
   228  		// Delete old Acl
   229  		_, err := ec2conn.DeleteNetworkAclEntry(d.Id(), remove.RuleNumber, remove.Egress)
   230  		if err != nil {
   231  			return fmt.Errorf("Error deleting %s entry: %s", entryType, err)
   232  		}
   233  	}
   234  
   235  	toBeCreated, err := expandNetworkAclEntries(ns.Difference(os).List(), entryType)
   236  	if err != nil {
   237  		return err
   238  	}
   239  	for _, add := range toBeCreated {
   240  		// Add new Acl entry
   241  		_, err := ec2conn.CreateNetworkAclEntry(d.Id(), &add)
   242  		if err != nil {
   243  			return fmt.Errorf("Error creating %s entry: %s", entryType, err)
   244  		}
   245  	}
   246  	return nil
   247  }
   248  
   249  func resourceAwsNetworkAclDelete(d *schema.ResourceData, meta interface{}) error {
   250  	ec2conn := meta.(*AWSClient).ec2conn
   251  
   252  	log.Printf("[INFO] Deleting Network Acl: %s", d.Id())
   253  	return resource.Retry(5*time.Minute, func() error {
   254  		if _, err := ec2conn.DeleteNetworkAcl(d.Id()); err != nil {
   255  			ec2err := err.(*ec2.Error)
   256  			switch ec2err.Code {
   257  			case "InvalidNetworkAclID.NotFound":
   258  				return nil
   259  			case "DependencyViolation":
   260  				// In case of dependency violation, we remove the association between subnet and network acl.
   261  				// This means the subnet is attached to default acl of vpc.
   262  				association, err := findNetworkAclAssociation(d.Get("subnet_id").(string), ec2conn)
   263  				if err != nil {
   264  					return fmt.Errorf("Dependency violation: Cannot delete acl %s: %s", d.Id(), err)
   265  				}
   266  				defaultAcl, err := getDefaultNetworkAcl(d.Get("vpc_id").(string), ec2conn)
   267  				if err != nil {
   268  					return fmt.Errorf("Dependency violation: Cannot delete acl %s: %s", d.Id(), err)
   269  				}
   270  				_, err = ec2conn.ReplaceNetworkAclAssociation(association.NetworkAclAssociationId, defaultAcl.NetworkAclId)
   271  				return resource.RetryError{err}
   272  			default:
   273  				// Any other error, we want to quit the retry loop immediately
   274  				return resource.RetryError{err}
   275  			}
   276  		}
   277  		log.Printf("[Info] Deleted network ACL %s successfully", d.Id())
   278  		return nil
   279  	})
   280  }
   281  
   282  func resourceAwsNetworkAclEntryHash(v interface{}) int {
   283  	var buf bytes.Buffer
   284  	m := v.(map[string]interface{})
   285  	buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int)))
   286  	buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int)))
   287  	buf.WriteString(fmt.Sprintf("%d-", m["rule_no"].(int)))
   288  	buf.WriteString(fmt.Sprintf("%s-", m["action"].(string)))
   289  	buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string)))
   290  	buf.WriteString(fmt.Sprintf("%s-", m["cidr_block"].(string)))
   291  
   292  	if v, ok := m["ssl_certificate_id"]; ok {
   293  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   294  	}
   295  
   296  	return hashcode.String(buf.String())
   297  }
   298  
   299  func getDefaultNetworkAcl(vpc_id string, ec2conn *ec2.EC2) (defaultAcl *ec2.NetworkAcl, err error) {
   300  	filter := ec2.NewFilter()
   301  	filter.Add("default", "true")
   302  	filter.Add("vpc-id", vpc_id)
   303  
   304  	resp, err := ec2conn.NetworkAcls([]string{}, filter)
   305  
   306  	if err != nil {
   307  		return nil, err
   308  	}
   309  	return &resp.NetworkAcls[0], nil
   310  }
   311  
   312  func findNetworkAclAssociation(subnetId string, ec2conn *ec2.EC2) (networkAclAssociation *ec2.NetworkAclAssociation, err error) {
   313  	filter := ec2.NewFilter()
   314  	filter.Add("association.subnet-id", subnetId)
   315  
   316  	resp, err := ec2conn.NetworkAcls([]string{}, filter)
   317  
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  	for _, association := range resp.NetworkAcls[0].AssociationSet {
   322  		if association.SubnetId == subnetId {
   323  			return &association, nil
   324  		}
   325  	}
   326  	return nil, fmt.Errorf("could not find association for subnet %s ", subnetId)
   327  }