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 }