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