github.com/i0n/terraform@v0.4.3-0.20150506151324-010a39a58ec1/builtin/providers/aws/resource_aws_security_group_rule.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "sort" 8 "strings" 9 10 "github.com/awslabs/aws-sdk-go/aws" 11 "github.com/awslabs/aws-sdk-go/service/ec2" 12 "github.com/hashicorp/terraform/helper/hashcode" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceAwsSecurityGroupRule() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceAwsSecurityGroupRuleCreate, 19 Read: resourceAwsSecurityGroupRuleRead, 20 Delete: resourceAwsSecurityGroupRuleDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "type": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 Description: "Type of rule, ingress (inbound) or egress (outbound).", 28 }, 29 30 "from_port": &schema.Schema{ 31 Type: schema.TypeInt, 32 Required: true, 33 ForceNew: true, 34 }, 35 36 "to_port": &schema.Schema{ 37 Type: schema.TypeInt, 38 Required: true, 39 ForceNew: true, 40 }, 41 42 "protocol": &schema.Schema{ 43 Type: schema.TypeString, 44 Required: true, 45 ForceNew: true, 46 }, 47 48 "cidr_blocks": &schema.Schema{ 49 Type: schema.TypeList, 50 Optional: true, 51 ForceNew: true, 52 Elem: &schema.Schema{Type: schema.TypeString}, 53 }, 54 55 "security_group_id": &schema.Schema{ 56 Type: schema.TypeString, 57 Required: true, 58 ForceNew: true, 59 }, 60 61 "source_security_group_id": &schema.Schema{ 62 Type: schema.TypeString, 63 Optional: true, 64 ForceNew: true, 65 Computed: true, 66 }, 67 68 "self": &schema.Schema{ 69 Type: schema.TypeBool, 70 Optional: true, 71 Default: false, 72 ForceNew: true, 73 }, 74 }, 75 } 76 } 77 78 func resourceAwsSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error { 79 conn := meta.(*AWSClient).ec2conn 80 sg_id := d.Get("security_group_id").(string) 81 sg, err := findResourceSecurityGroup(conn, sg_id) 82 83 if err != nil { 84 return fmt.Errorf("sorry") 85 } 86 87 perm := expandIPPerm(d, sg) 88 89 ruleType := d.Get("type").(string) 90 91 switch ruleType { 92 case "ingress": 93 log.Printf("[DEBUG] Authorizing security group %s %s rule: %#v", 94 sg_id, "Ingress", perm) 95 96 req := &ec2.AuthorizeSecurityGroupIngressInput{ 97 GroupID: sg.GroupID, 98 IPPermissions: []*ec2.IPPermission{perm}, 99 } 100 101 if sg.VPCID == nil || *sg.VPCID == "" { 102 req.GroupID = nil 103 req.GroupName = sg.GroupName 104 } 105 106 _, err := conn.AuthorizeSecurityGroupIngress(req) 107 108 if err != nil { 109 return fmt.Errorf( 110 "Error authorizing security group %s rules: %s", 111 "rules", err) 112 } 113 114 case "egress": 115 log.Printf("[DEBUG] Authorizing security group %s %s rule: %#v", 116 sg_id, "Egress", perm) 117 118 req := &ec2.AuthorizeSecurityGroupEgressInput{ 119 GroupID: sg.GroupID, 120 IPPermissions: []*ec2.IPPermission{perm}, 121 } 122 123 _, err = conn.AuthorizeSecurityGroupEgress(req) 124 125 if err != nil { 126 return fmt.Errorf( 127 "Error authorizing security group %s rules: %s", 128 "rules", err) 129 } 130 131 default: 132 return fmt.Errorf("Security Group Rule must be type 'ingress' or type 'egress'") 133 } 134 135 d.SetId(ipPermissionIDHash(ruleType, perm)) 136 137 return resourceAwsSecurityGroupRuleRead(d, meta) 138 } 139 140 func resourceAwsSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error { 141 conn := meta.(*AWSClient).ec2conn 142 sg_id := d.Get("security_group_id").(string) 143 sg, err := findResourceSecurityGroup(conn, sg_id) 144 if err != nil { 145 d.SetId("") 146 } 147 148 var rule *ec2.IPPermission 149 ruleType := d.Get("type").(string) 150 var rl []*ec2.IPPermission 151 switch ruleType { 152 case "ingress": 153 rl = sg.IPPermissions 154 default: 155 rl = sg.IPPermissionsEgress 156 } 157 158 for _, r := range rl { 159 if d.Id() == ipPermissionIDHash(ruleType, r) { 160 rule = r 161 } 162 } 163 164 if rule == nil { 165 log.Printf("[DEBUG] Unable to find matching %s Security Group Rule for Group %s", 166 ruleType, sg_id) 167 d.SetId("") 168 return nil 169 } 170 171 d.Set("from_port", rule.FromPort) 172 d.Set("to_port", rule.ToPort) 173 d.Set("protocol", rule.IPProtocol) 174 d.Set("type", ruleType) 175 176 var cb []string 177 for _, c := range rule.IPRanges { 178 cb = append(cb, *c.CIDRIP) 179 } 180 181 d.Set("cidr_blocks", cb) 182 183 if len(rule.UserIDGroupPairs) > 0 { 184 s := rule.UserIDGroupPairs[0] 185 d.Set("source_security_group_id", *s.GroupID) 186 } 187 188 return nil 189 } 190 191 func resourceAwsSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error { 192 conn := meta.(*AWSClient).ec2conn 193 sg_id := d.Get("security_group_id").(string) 194 sg, err := findResourceSecurityGroup(conn, sg_id) 195 196 if err != nil { 197 return fmt.Errorf("sorry") 198 } 199 200 perm := expandIPPerm(d, sg) 201 ruleType := d.Get("type").(string) 202 switch ruleType { 203 case "ingress": 204 log.Printf("[DEBUG] Revoking security group %#v %s rule: %#v", 205 sg_id, "ingress", perm) 206 req := &ec2.RevokeSecurityGroupIngressInput{ 207 GroupID: sg.GroupID, 208 IPPermissions: []*ec2.IPPermission{perm}, 209 } 210 211 _, err = conn.RevokeSecurityGroupIngress(req) 212 213 if err != nil { 214 return fmt.Errorf( 215 "Error revoking security group %s rules: %s", 216 sg_id, err) 217 } 218 case "egress": 219 220 log.Printf("[DEBUG] Revoking security group %#v %s rule: %#v", 221 sg_id, "egress", perm) 222 req := &ec2.RevokeSecurityGroupEgressInput{ 223 GroupID: sg.GroupID, 224 IPPermissions: []*ec2.IPPermission{perm}, 225 } 226 227 _, err = conn.RevokeSecurityGroupEgress(req) 228 229 if err != nil { 230 return fmt.Errorf( 231 "Error revoking security group %s rules: %s", 232 sg_id, err) 233 } 234 } 235 236 d.SetId("") 237 238 return nil 239 } 240 241 func findResourceSecurityGroup(conn *ec2.EC2, id string) (*ec2.SecurityGroup, error) { 242 req := &ec2.DescribeSecurityGroupsInput{ 243 GroupIDs: []*string{aws.String(id)}, 244 } 245 resp, err := conn.DescribeSecurityGroups(req) 246 if err != nil { 247 if ec2err, ok := err.(aws.APIError); ok { 248 if ec2err.Code == "InvalidSecurityGroupID.NotFound" || 249 ec2err.Code == "InvalidGroup.NotFound" { 250 resp = nil 251 err = nil 252 } 253 } 254 255 if err != nil { 256 log.Printf("Error on findResourceSecurityGroup: %s", err) 257 return nil, err 258 } 259 } 260 261 return resp.SecurityGroups[0], nil 262 } 263 264 func ipPermissionIDHash(ruleType string, ip *ec2.IPPermission) string { 265 var buf bytes.Buffer 266 // for egress rules, an TCP rule of -1 is automatically added, in which case 267 // the to and from ports will be nil. We don't record this rule locally. 268 if ip.IPProtocol != nil && *ip.IPProtocol != "-1" { 269 buf.WriteString(fmt.Sprintf("%d-", *ip.FromPort)) 270 buf.WriteString(fmt.Sprintf("%d-", *ip.ToPort)) 271 buf.WriteString(fmt.Sprintf("%s-", *ip.IPProtocol)) 272 } 273 buf.WriteString(fmt.Sprintf("%s-", ruleType)) 274 275 // We need to make sure to sort the strings below so that we always 276 // generate the same hash code no matter what is in the set. 277 if len(ip.IPRanges) > 0 { 278 s := make([]string, len(ip.IPRanges)) 279 for i, r := range ip.IPRanges { 280 s[i] = *r.CIDRIP 281 } 282 sort.Strings(s) 283 284 for _, v := range s { 285 buf.WriteString(fmt.Sprintf("%s-", v)) 286 } 287 } 288 289 return fmt.Sprintf("sg-%d", hashcode.String(buf.String())) 290 } 291 292 func expandIPPerm(d *schema.ResourceData, sg *ec2.SecurityGroup) *ec2.IPPermission { 293 var perm ec2.IPPermission 294 295 perm.FromPort = aws.Long(int64(d.Get("from_port").(int))) 296 perm.ToPort = aws.Long(int64(d.Get("to_port").(int))) 297 perm.IPProtocol = aws.String(d.Get("protocol").(string)) 298 299 var groups []string 300 if raw, ok := d.GetOk("source_security_group_id"); ok { 301 groups = append(groups, raw.(string)) 302 } 303 304 if v, ok := d.GetOk("self"); ok && v.(bool) { 305 if sg.VPCID != nil && *sg.VPCID != "" { 306 groups = append(groups, *sg.GroupID) 307 } else { 308 groups = append(groups, *sg.GroupName) 309 } 310 } 311 312 if len(groups) > 0 { 313 perm.UserIDGroupPairs = make([]*ec2.UserIDGroupPair, len(groups)) 314 for i, name := range groups { 315 ownerId, id := "", name 316 if items := strings.Split(id, "/"); len(items) > 1 { 317 ownerId, id = items[0], items[1] 318 } 319 320 perm.UserIDGroupPairs[i] = &ec2.UserIDGroupPair{ 321 GroupID: aws.String(id), 322 UserID: aws.String(ownerId), 323 } 324 325 if sg.VPCID == nil || *sg.VPCID == "" { 326 perm.UserIDGroupPairs[i].GroupID = nil 327 perm.UserIDGroupPairs[i].GroupName = aws.String(id) 328 perm.UserIDGroupPairs[i].UserID = nil 329 } 330 } 331 } 332 333 if raw, ok := d.GetOk("cidr_blocks"); ok { 334 list := raw.([]interface{}) 335 perm.IPRanges = make([]*ec2.IPRange, len(list)) 336 for i, v := range list { 337 perm.IPRanges[i] = &ec2.IPRange{CIDRIP: aws.String(v.(string))} 338 } 339 } 340 341 return &perm 342 }