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