github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/aws/resource_aws_db_security_group.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "strings" 8 "time" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/service/iam" 13 "github.com/aws/aws-sdk-go/service/rds" 14 "github.com/hashicorp/go-multierror" 15 "github.com/hashicorp/terraform/helper/hashcode" 16 "github.com/hashicorp/terraform/helper/resource" 17 "github.com/hashicorp/terraform/helper/schema" 18 ) 19 20 func resourceAwsDbSecurityGroup() *schema.Resource { 21 return &schema.Resource{ 22 Create: resourceAwsDbSecurityGroupCreate, 23 Read: resourceAwsDbSecurityGroupRead, 24 Update: resourceAwsDbSecurityGroupUpdate, 25 Delete: resourceAwsDbSecurityGroupDelete, 26 27 Schema: map[string]*schema.Schema{ 28 "arn": &schema.Schema{ 29 Type: schema.TypeString, 30 Computed: true, 31 }, 32 33 "name": &schema.Schema{ 34 Type: schema.TypeString, 35 Required: true, 36 ForceNew: true, 37 }, 38 39 "description": &schema.Schema{ 40 Type: schema.TypeString, 41 Required: true, 42 ForceNew: true, 43 }, 44 45 "ingress": &schema.Schema{ 46 Type: schema.TypeSet, 47 Required: true, 48 ForceNew: true, 49 Elem: &schema.Resource{ 50 Schema: map[string]*schema.Schema{ 51 "cidr": &schema.Schema{ 52 Type: schema.TypeString, 53 Optional: true, 54 }, 55 56 "security_group_name": &schema.Schema{ 57 Type: schema.TypeString, 58 Optional: true, 59 Computed: true, 60 }, 61 62 "security_group_id": &schema.Schema{ 63 Type: schema.TypeString, 64 Optional: true, 65 Computed: true, 66 }, 67 68 "security_group_owner_id": &schema.Schema{ 69 Type: schema.TypeString, 70 Optional: true, 71 Computed: true, 72 }, 73 }, 74 }, 75 Set: resourceAwsDbSecurityGroupIngressHash, 76 }, 77 78 "tags": tagsSchema(), 79 }, 80 } 81 } 82 83 func resourceAwsDbSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { 84 conn := meta.(*AWSClient).rdsconn 85 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 86 87 var err error 88 var errs []error 89 90 opts := rds.CreateDBSecurityGroupInput{ 91 DBSecurityGroupName: aws.String(d.Get("name").(string)), 92 DBSecurityGroupDescription: aws.String(d.Get("description").(string)), 93 Tags: tags, 94 } 95 96 log.Printf("[DEBUG] DB Security Group create configuration: %#v", opts) 97 _, err = conn.CreateDBSecurityGroup(&opts) 98 if err != nil { 99 return fmt.Errorf("Error creating DB Security Group: %s", err) 100 } 101 102 d.SetId(d.Get("name").(string)) 103 104 log.Printf("[INFO] DB Security Group ID: %s", d.Id()) 105 106 sg, err := resourceAwsDbSecurityGroupRetrieve(d, meta) 107 if err != nil { 108 return err 109 } 110 111 ingresses := d.Get("ingress").(*schema.Set) 112 for _, ing := range ingresses.List() { 113 err := resourceAwsDbSecurityGroupAuthorizeRule(ing, *sg.DBSecurityGroupName, conn) 114 if err != nil { 115 errs = append(errs, err) 116 } 117 } 118 119 if len(errs) > 0 { 120 return &multierror.Error{Errors: errs} 121 } 122 123 log.Println( 124 "[INFO] Waiting for Ingress Authorizations to be authorized") 125 126 stateConf := &resource.StateChangeConf{ 127 Pending: []string{"authorizing"}, 128 Target: []string{"authorized"}, 129 Refresh: resourceAwsDbSecurityGroupStateRefreshFunc(d, meta), 130 Timeout: 10 * time.Minute, 131 } 132 133 // Wait, catching any errors 134 _, err = stateConf.WaitForState() 135 if err != nil { 136 return err 137 } 138 139 return resourceAwsDbSecurityGroupRead(d, meta) 140 } 141 142 func resourceAwsDbSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { 143 sg, err := resourceAwsDbSecurityGroupRetrieve(d, meta) 144 if err != nil { 145 return err 146 } 147 148 d.Set("name", *sg.DBSecurityGroupName) 149 d.Set("description", *sg.DBSecurityGroupDescription) 150 151 // Create an empty schema.Set to hold all ingress rules 152 rules := &schema.Set{ 153 F: resourceAwsDbSecurityGroupIngressHash, 154 } 155 156 for _, v := range sg.IPRanges { 157 rule := map[string]interface{}{"cidr": *v.CIDRIP} 158 rules.Add(rule) 159 } 160 161 for _, g := range sg.EC2SecurityGroups { 162 rule := map[string]interface{}{ 163 "security_group_name": *g.EC2SecurityGroupName, 164 "security_group_id": *g.EC2SecurityGroupId, 165 "security_group_owner_id": *g.EC2SecurityGroupOwnerId, 166 } 167 rules.Add(rule) 168 } 169 170 d.Set("ingress", rules) 171 172 conn := meta.(*AWSClient).rdsconn 173 arn, err := buildRDSSecurityGroupARN(d, meta) 174 if err != nil { 175 name := "<empty>" 176 if sg.DBSecurityGroupName != nil && *sg.DBSecurityGroupName != "" { 177 name = *sg.DBSecurityGroupName 178 } 179 log.Printf("[DEBUG] Error building ARN for DB Security Group, not setting Tags for DB Security Group %s", name) 180 } else { 181 d.Set("arn", arn) 182 resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{ 183 ResourceName: aws.String(arn), 184 }) 185 186 if err != nil { 187 log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn) 188 } 189 190 var dt []*rds.Tag 191 if len(resp.TagList) > 0 { 192 dt = resp.TagList 193 } 194 d.Set("tags", tagsToMapRDS(dt)) 195 } 196 197 return nil 198 } 199 200 func resourceAwsDbSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error { 201 conn := meta.(*AWSClient).rdsconn 202 203 d.Partial(true) 204 if arn, err := buildRDSSecurityGroupARN(d, meta); err == nil { 205 if err := setTagsRDS(conn, d, arn); err != nil { 206 return err 207 } else { 208 d.SetPartial("tags") 209 } 210 } 211 d.Partial(false) 212 213 return resourceAwsDbSecurityGroupRead(d, meta) 214 } 215 216 func resourceAwsDbSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { 217 conn := meta.(*AWSClient).rdsconn 218 219 log.Printf("[DEBUG] DB Security Group destroy: %v", d.Id()) 220 221 opts := rds.DeleteDBSecurityGroupInput{DBSecurityGroupName: aws.String(d.Id())} 222 223 log.Printf("[DEBUG] DB Security Group destroy configuration: %v", opts) 224 _, err := conn.DeleteDBSecurityGroup(&opts) 225 226 if err != nil { 227 newerr, ok := err.(awserr.Error) 228 if ok && newerr.Code() == "InvalidDBSecurityGroup.NotFound" { 229 return nil 230 } 231 return err 232 } 233 234 return nil 235 } 236 237 func resourceAwsDbSecurityGroupRetrieve(d *schema.ResourceData, meta interface{}) (*rds.DBSecurityGroup, error) { 238 conn := meta.(*AWSClient).rdsconn 239 240 opts := rds.DescribeDBSecurityGroupsInput{ 241 DBSecurityGroupName: aws.String(d.Id()), 242 } 243 244 log.Printf("[DEBUG] DB Security Group describe configuration: %#v", opts) 245 246 resp, err := conn.DescribeDBSecurityGroups(&opts) 247 248 if err != nil { 249 return nil, fmt.Errorf("Error retrieving DB Security Groups: %s", err) 250 } 251 252 if len(resp.DBSecurityGroups) != 1 || 253 *resp.DBSecurityGroups[0].DBSecurityGroupName != d.Id() { 254 return nil, fmt.Errorf("Unable to find DB Security Group: %#v", resp.DBSecurityGroups) 255 } 256 257 return resp.DBSecurityGroups[0], nil 258 } 259 260 // Authorizes the ingress rule on the db security group 261 func resourceAwsDbSecurityGroupAuthorizeRule(ingress interface{}, dbSecurityGroupName string, conn *rds.RDS) error { 262 ing := ingress.(map[string]interface{}) 263 264 opts := rds.AuthorizeDBSecurityGroupIngressInput{ 265 DBSecurityGroupName: aws.String(dbSecurityGroupName), 266 } 267 268 if attr, ok := ing["cidr"]; ok && attr != "" { 269 opts.CIDRIP = aws.String(attr.(string)) 270 } 271 272 if attr, ok := ing["security_group_name"]; ok && attr != "" { 273 opts.EC2SecurityGroupName = aws.String(attr.(string)) 274 } 275 276 if attr, ok := ing["security_group_id"]; ok && attr != "" { 277 opts.EC2SecurityGroupId = aws.String(attr.(string)) 278 } 279 280 if attr, ok := ing["security_group_owner_id"]; ok && attr != "" { 281 opts.EC2SecurityGroupOwnerId = aws.String(attr.(string)) 282 } 283 284 log.Printf("[DEBUG] Authorize ingress rule configuration: %#v", opts) 285 286 _, err := conn.AuthorizeDBSecurityGroupIngress(&opts) 287 288 if err != nil { 289 return fmt.Errorf("Error authorizing security group ingress: %s", err) 290 } 291 292 return nil 293 } 294 295 func resourceAwsDbSecurityGroupIngressHash(v interface{}) int { 296 var buf bytes.Buffer 297 m := v.(map[string]interface{}) 298 299 if v, ok := m["cidr"]; ok { 300 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 301 } 302 303 if v, ok := m["security_group_name"]; ok { 304 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 305 } 306 307 if v, ok := m["security_group_id"]; ok { 308 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 309 } 310 311 if v, ok := m["security_group_owner_id"]; ok { 312 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 313 } 314 315 return hashcode.String(buf.String()) 316 } 317 318 func resourceAwsDbSecurityGroupStateRefreshFunc( 319 d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { 320 return func() (interface{}, string, error) { 321 v, err := resourceAwsDbSecurityGroupRetrieve(d, meta) 322 323 if err != nil { 324 log.Printf("Error on retrieving DB Security Group when waiting: %s", err) 325 return nil, "", err 326 } 327 328 statuses := make([]string, 0, len(v.EC2SecurityGroups)+len(v.IPRanges)) 329 for _, ec2g := range v.EC2SecurityGroups { 330 statuses = append(statuses, *ec2g.Status) 331 } 332 for _, ips := range v.IPRanges { 333 statuses = append(statuses, *ips.Status) 334 } 335 336 for _, stat := range statuses { 337 // Not done 338 if stat != "authorized" { 339 return nil, "authorizing", nil 340 } 341 } 342 343 return v, "authorized", nil 344 } 345 } 346 347 func buildRDSSecurityGroupARN(d *schema.ResourceData, meta interface{}) (string, error) { 348 iamconn := meta.(*AWSClient).iamconn 349 region := meta.(*AWSClient).region 350 // An zero value GetUserInput{} defers to the currently logged in user 351 resp, err := iamconn.GetUser(&iam.GetUserInput{}) 352 if err != nil { 353 return "", err 354 } 355 userARN := *resp.User.Arn 356 accountID := strings.Split(userARN, ":")[4] 357 arn := fmt.Sprintf("arn:aws:rds:%s:%s:secgrp:%s", region, accountID, d.Id()) 358 return arn, nil 359 }