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 }