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