github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_default_network_acl.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/aws/aws-sdk-go/aws" 8 "github.com/aws/aws-sdk-go/service/ec2" 9 "github.com/hashicorp/terraform/helper/schema" 10 ) 11 12 // ACL Network ACLs all contain explicit deny-all rules that cannot be 13 // destroyed or changed by users. This rules are numbered very high to be a 14 // catch-all. 15 // See http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#default-network-acl 16 const ( 17 awsDefaultAclRuleNumberIpv4 = 32767 18 awsDefaultAclRuleNumberIpv6 = 32768 19 ) 20 21 func resourceAwsDefaultNetworkAcl() *schema.Resource { 22 return &schema.Resource{ 23 Create: resourceAwsDefaultNetworkAclCreate, 24 // We reuse aws_network_acl's read method, the operations are the same 25 Read: resourceAwsNetworkAclRead, 26 Delete: resourceAwsDefaultNetworkAclDelete, 27 Update: resourceAwsDefaultNetworkAclUpdate, 28 29 Schema: map[string]*schema.Schema{ 30 "vpc_id": &schema.Schema{ 31 Type: schema.TypeString, 32 Computed: true, 33 }, 34 "default_network_acl_id": &schema.Schema{ 35 Type: schema.TypeString, 36 Required: true, 37 ForceNew: true, 38 Computed: false, 39 }, 40 // We want explicit management of Subnets here, so we do not allow them to be 41 // computed. Instead, an empty config will enforce just that; removal of the 42 // any Subnets that have been assigned to the Default Network ACL. Because we 43 // can't actually remove them, this will be a continual plan until the 44 // Subnets are themselves destroyed or reassigned to a different Network 45 // ACL 46 "subnet_ids": &schema.Schema{ 47 Type: schema.TypeSet, 48 Optional: true, 49 Elem: &schema.Schema{Type: schema.TypeString}, 50 Set: schema.HashString, 51 }, 52 // We want explicit management of Rules here, so we do not allow them to be 53 // computed. Instead, an empty config will enforce just that; removal of the 54 // rules 55 "ingress": &schema.Schema{ 56 Type: schema.TypeSet, 57 Required: false, 58 Optional: true, 59 Elem: &schema.Resource{ 60 Schema: map[string]*schema.Schema{ 61 "from_port": &schema.Schema{ 62 Type: schema.TypeInt, 63 Required: true, 64 }, 65 "to_port": &schema.Schema{ 66 Type: schema.TypeInt, 67 Required: true, 68 }, 69 "rule_no": &schema.Schema{ 70 Type: schema.TypeInt, 71 Required: true, 72 }, 73 "action": &schema.Schema{ 74 Type: schema.TypeString, 75 Required: true, 76 }, 77 "protocol": &schema.Schema{ 78 Type: schema.TypeString, 79 Required: true, 80 }, 81 "cidr_block": &schema.Schema{ 82 Type: schema.TypeString, 83 Optional: true, 84 }, 85 "icmp_type": &schema.Schema{ 86 Type: schema.TypeInt, 87 Optional: true, 88 }, 89 "icmp_code": &schema.Schema{ 90 Type: schema.TypeInt, 91 Optional: true, 92 }, 93 }, 94 }, 95 Set: resourceAwsNetworkAclEntryHash, 96 }, 97 "egress": &schema.Schema{ 98 Type: schema.TypeSet, 99 Required: false, 100 Optional: true, 101 Elem: &schema.Resource{ 102 Schema: map[string]*schema.Schema{ 103 "from_port": &schema.Schema{ 104 Type: schema.TypeInt, 105 Required: true, 106 }, 107 "to_port": &schema.Schema{ 108 Type: schema.TypeInt, 109 Required: true, 110 }, 111 "rule_no": &schema.Schema{ 112 Type: schema.TypeInt, 113 Required: true, 114 }, 115 "action": &schema.Schema{ 116 Type: schema.TypeString, 117 Required: true, 118 }, 119 "protocol": &schema.Schema{ 120 Type: schema.TypeString, 121 Required: true, 122 }, 123 "cidr_block": &schema.Schema{ 124 Type: schema.TypeString, 125 Optional: true, 126 }, 127 "icmp_type": &schema.Schema{ 128 Type: schema.TypeInt, 129 Optional: true, 130 }, 131 "icmp_code": &schema.Schema{ 132 Type: schema.TypeInt, 133 Optional: true, 134 }, 135 }, 136 }, 137 Set: resourceAwsNetworkAclEntryHash, 138 }, 139 140 "tags": tagsSchema(), 141 }, 142 } 143 } 144 145 func resourceAwsDefaultNetworkAclCreate(d *schema.ResourceData, meta interface{}) error { 146 d.SetId(d.Get("default_network_acl_id").(string)) 147 148 // revoke all default and pre-existing rules on the default network acl. 149 // In the UPDATE method, we'll apply only the rules in the configuration. 150 log.Printf("[DEBUG] Revoking default ingress and egress rules for Default Network ACL for %s", d.Id()) 151 err := revokeAllNetworkACLEntries(d.Id(), meta) 152 if err != nil { 153 return err 154 } 155 156 return resourceAwsDefaultNetworkAclUpdate(d, meta) 157 } 158 159 func resourceAwsDefaultNetworkAclUpdate(d *schema.ResourceData, meta interface{}) error { 160 conn := meta.(*AWSClient).ec2conn 161 d.Partial(true) 162 163 if d.HasChange("ingress") { 164 err := updateNetworkAclEntries(d, "ingress", conn) 165 if err != nil { 166 return err 167 } 168 } 169 170 if d.HasChange("egress") { 171 err := updateNetworkAclEntries(d, "egress", conn) 172 if err != nil { 173 return err 174 } 175 } 176 177 if d.HasChange("subnet_ids") { 178 o, n := d.GetChange("subnet_ids") 179 if o == nil { 180 o = new(schema.Set) 181 } 182 if n == nil { 183 n = new(schema.Set) 184 } 185 186 os := o.(*schema.Set) 187 ns := n.(*schema.Set) 188 189 remove := os.Difference(ns).List() 190 add := ns.Difference(os).List() 191 192 if len(remove) > 0 { 193 // 194 // NO-OP 195 // 196 // Subnets *must* belong to a Network ACL. Subnets are not "removed" from 197 // Network ACLs, instead their association is replaced. In a normal 198 // Network ACL, any removal of a Subnet is done by replacing the 199 // Subnet/ACL association with an association between the Subnet and the 200 // Default Network ACL. Because we're managing the default here, we cannot 201 // do that, so we simply log a NO-OP. In order to remove the Subnet here, 202 // it must be destroyed, or assigned to different Network ACL. Those 203 // operations are not handled here 204 log.Printf("[WARN] Cannot remove subnets from the Default Network ACL. They must be re-assigned or destroyed") 205 } 206 207 if len(add) > 0 { 208 for _, a := range add { 209 association, err := findNetworkAclAssociation(a.(string), conn) 210 if err != nil { 211 return fmt.Errorf("Failed to find acl association: acl %s with subnet %s: %s", d.Id(), a, err) 212 } 213 log.Printf("[DEBUG] Updating Network Association for Default Network ACL (%s) and Subnet (%s)", d.Id(), a.(string)) 214 _, err = conn.ReplaceNetworkAclAssociation(&ec2.ReplaceNetworkAclAssociationInput{ 215 AssociationId: association.NetworkAclAssociationId, 216 NetworkAclId: aws.String(d.Id()), 217 }) 218 if err != nil { 219 return err 220 } 221 } 222 } 223 } 224 225 if err := setTags(conn, d); err != nil { 226 return err 227 } else { 228 d.SetPartial("tags") 229 } 230 231 d.Partial(false) 232 // Re-use the exiting Network ACL Resources READ method 233 return resourceAwsNetworkAclRead(d, meta) 234 } 235 236 func resourceAwsDefaultNetworkAclDelete(d *schema.ResourceData, meta interface{}) error { 237 log.Printf("[WARN] Cannot destroy Default Network ACL. Terraform will remove this resource from the state file, however resources may remain.") 238 d.SetId("") 239 return nil 240 } 241 242 // revokeAllNetworkACLEntries revoke all ingress and egress rules that the Default 243 // Network ACL currently has 244 func revokeAllNetworkACLEntries(netaclId string, meta interface{}) error { 245 conn := meta.(*AWSClient).ec2conn 246 247 resp, err := conn.DescribeNetworkAcls(&ec2.DescribeNetworkAclsInput{ 248 NetworkAclIds: []*string{aws.String(netaclId)}, 249 }) 250 251 if err != nil { 252 log.Printf("[DEBUG] Error looking up Network ACL: %s", err) 253 return err 254 } 255 256 if resp == nil { 257 return fmt.Errorf("[ERR] Error looking up Default Network ACL Entries: No results") 258 } 259 260 networkAcl := resp.NetworkAcls[0] 261 for _, e := range networkAcl.Entries { 262 // Skip the default rules added by AWS. They can be neither 263 // configured or deleted by users. See http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#default-network-acl 264 if *e.RuleNumber == awsDefaultAclRuleNumberIpv4 || 265 *e.RuleNumber == awsDefaultAclRuleNumberIpv6 { 266 continue 267 } 268 269 // track if this is an egress or ingress rule, for logging purposes 270 rt := "ingress" 271 if *e.Egress == true { 272 rt = "egress" 273 } 274 275 log.Printf("[DEBUG] Destroying Network ACL (%s) Entry number (%d)", rt, int(*e.RuleNumber)) 276 _, err := conn.DeleteNetworkAclEntry(&ec2.DeleteNetworkAclEntryInput{ 277 NetworkAclId: aws.String(netaclId), 278 RuleNumber: e.RuleNumber, 279 Egress: e.Egress, 280 }) 281 if err != nil { 282 return fmt.Errorf("Error deleting entry (%s): %s", e, err) 283 } 284 } 285 286 return nil 287 }