github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/builtin/providers/aws/structure.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "reflect" 8 "sort" 9 "strconv" 10 "strings" 11 12 "github.com/aws/aws-sdk-go/aws" 13 "github.com/aws/aws-sdk-go/service/apigateway" 14 "github.com/aws/aws-sdk-go/service/autoscaling" 15 "github.com/aws/aws-sdk-go/service/cloudformation" 16 "github.com/aws/aws-sdk-go/service/cloudwatchlogs" 17 "github.com/aws/aws-sdk-go/service/directoryservice" 18 "github.com/aws/aws-sdk-go/service/ec2" 19 "github.com/aws/aws-sdk-go/service/ecs" 20 "github.com/aws/aws-sdk-go/service/elasticache" 21 "github.com/aws/aws-sdk-go/service/elasticbeanstalk" 22 elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice" 23 "github.com/aws/aws-sdk-go/service/elb" 24 "github.com/aws/aws-sdk-go/service/lambda" 25 "github.com/aws/aws-sdk-go/service/rds" 26 "github.com/aws/aws-sdk-go/service/redshift" 27 "github.com/aws/aws-sdk-go/service/route53" 28 "github.com/hashicorp/terraform/helper/schema" 29 ) 30 31 // Takes the result of flatmap.Expand for an array of listeners and 32 // returns ELB API compatible objects 33 func expandListeners(configured []interface{}) ([]*elb.Listener, error) { 34 listeners := make([]*elb.Listener, 0, len(configured)) 35 36 // Loop over our configured listeners and create 37 // an array of aws-sdk-go compatabile objects 38 for _, lRaw := range configured { 39 data := lRaw.(map[string]interface{}) 40 41 ip := int64(data["instance_port"].(int)) 42 lp := int64(data["lb_port"].(int)) 43 l := &elb.Listener{ 44 InstancePort: &ip, 45 InstanceProtocol: aws.String(data["instance_protocol"].(string)), 46 LoadBalancerPort: &lp, 47 Protocol: aws.String(data["lb_protocol"].(string)), 48 } 49 50 if v, ok := data["ssl_certificate_id"]; ok { 51 l.SSLCertificateId = aws.String(v.(string)) 52 } 53 54 var valid bool 55 if l.SSLCertificateId != nil && *l.SSLCertificateId != "" { 56 // validate the protocol is correct 57 for _, p := range []string{"https", "ssl"} { 58 if (strings.ToLower(*l.InstanceProtocol) == p) || (strings.ToLower(*l.Protocol) == p) { 59 valid = true 60 } 61 } 62 } else { 63 valid = true 64 } 65 66 if valid { 67 listeners = append(listeners, l) 68 } else { 69 return nil, fmt.Errorf("[ERR] ELB Listener: ssl_certificate_id may be set only when protocol is 'https' or 'ssl'") 70 } 71 } 72 73 return listeners, nil 74 } 75 76 // Takes the result of flatmap. Expand for an array of listeners and 77 // returns ECS Volume compatible objects 78 func expandEcsVolumes(configured []interface{}) ([]*ecs.Volume, error) { 79 volumes := make([]*ecs.Volume, 0, len(configured)) 80 81 // Loop over our configured volumes and create 82 // an array of aws-sdk-go compatible objects 83 for _, lRaw := range configured { 84 data := lRaw.(map[string]interface{}) 85 86 l := &ecs.Volume{ 87 Name: aws.String(data["name"].(string)), 88 } 89 90 hostPath := data["host_path"].(string) 91 if hostPath != "" { 92 l.Host = &ecs.HostVolumeProperties{ 93 SourcePath: aws.String(hostPath), 94 } 95 } 96 97 volumes = append(volumes, l) 98 } 99 100 return volumes, nil 101 } 102 103 // Takes JSON in a string. Decodes JSON into 104 // an array of ecs.ContainerDefinition compatible objects 105 func expandEcsContainerDefinitions(rawDefinitions string) ([]*ecs.ContainerDefinition, error) { 106 var definitions []*ecs.ContainerDefinition 107 108 err := json.Unmarshal([]byte(rawDefinitions), &definitions) 109 if err != nil { 110 return nil, fmt.Errorf("Error decoding JSON: %s", err) 111 } 112 113 return definitions, nil 114 } 115 116 // Takes the result of flatmap. Expand for an array of load balancers and 117 // returns ecs.LoadBalancer compatible objects 118 func expandEcsLoadBalancers(configured []interface{}) []*ecs.LoadBalancer { 119 loadBalancers := make([]*ecs.LoadBalancer, 0, len(configured)) 120 121 // Loop over our configured load balancers and create 122 // an array of aws-sdk-go compatible objects 123 for _, lRaw := range configured { 124 data := lRaw.(map[string]interface{}) 125 126 l := &ecs.LoadBalancer{ 127 ContainerName: aws.String(data["container_name"].(string)), 128 ContainerPort: aws.Int64(int64(data["container_port"].(int))), 129 LoadBalancerName: aws.String(data["elb_name"].(string)), 130 } 131 132 loadBalancers = append(loadBalancers, l) 133 } 134 135 return loadBalancers 136 } 137 138 // Takes the result of flatmap.Expand for an array of ingress/egress security 139 // group rules and returns EC2 API compatible objects. This function will error 140 // if it finds invalid permissions input, namely a protocol of "-1" with either 141 // to_port or from_port set to a non-zero value. 142 func expandIPPerms( 143 group *ec2.SecurityGroup, configured []interface{}) ([]*ec2.IpPermission, error) { 144 vpc := group.VpcId != nil && *group.VpcId != "" 145 146 perms := make([]*ec2.IpPermission, len(configured)) 147 for i, mRaw := range configured { 148 var perm ec2.IpPermission 149 m := mRaw.(map[string]interface{}) 150 151 perm.FromPort = aws.Int64(int64(m["from_port"].(int))) 152 perm.ToPort = aws.Int64(int64(m["to_port"].(int))) 153 perm.IpProtocol = aws.String(m["protocol"].(string)) 154 155 // When protocol is "-1", AWS won't store any ports for the 156 // rule, but also won't error if the user specifies ports other 157 // than '0'. Force the user to make a deliberate '0' port 158 // choice when specifying a "-1" protocol, and tell them about 159 // AWS's behavior in the error message. 160 if *perm.IpProtocol == "-1" && (*perm.FromPort != 0 || *perm.ToPort != 0) { 161 return nil, fmt.Errorf( 162 "from_port (%d) and to_port (%d) must both be 0 to use the the 'ALL' \"-1\" protocol!", 163 *perm.FromPort, *perm.ToPort) 164 } 165 166 var groups []string 167 if raw, ok := m["security_groups"]; ok { 168 list := raw.(*schema.Set).List() 169 for _, v := range list { 170 groups = append(groups, v.(string)) 171 } 172 } 173 if v, ok := m["self"]; ok && v.(bool) { 174 if vpc { 175 groups = append(groups, *group.GroupId) 176 } else { 177 groups = append(groups, *group.GroupName) 178 } 179 } 180 181 if len(groups) > 0 { 182 perm.UserIdGroupPairs = make([]*ec2.UserIdGroupPair, len(groups)) 183 for i, name := range groups { 184 ownerId, id := "", name 185 if items := strings.Split(id, "/"); len(items) > 1 { 186 ownerId, id = items[0], items[1] 187 } 188 189 perm.UserIdGroupPairs[i] = &ec2.UserIdGroupPair{ 190 GroupId: aws.String(id), 191 } 192 193 if ownerId != "" { 194 perm.UserIdGroupPairs[i].UserId = aws.String(ownerId) 195 } 196 197 if !vpc { 198 perm.UserIdGroupPairs[i].GroupId = nil 199 perm.UserIdGroupPairs[i].GroupName = aws.String(id) 200 } 201 } 202 } 203 204 if raw, ok := m["cidr_blocks"]; ok { 205 list := raw.([]interface{}) 206 for _, v := range list { 207 perm.IpRanges = append(perm.IpRanges, &ec2.IpRange{CidrIp: aws.String(v.(string))}) 208 } 209 } 210 211 perms[i] = &perm 212 } 213 214 return perms, nil 215 } 216 217 // Takes the result of flatmap.Expand for an array of parameters and 218 // returns Parameter API compatible objects 219 func expandParameters(configured []interface{}) ([]*rds.Parameter, error) { 220 var parameters []*rds.Parameter 221 222 // Loop over our configured parameters and create 223 // an array of aws-sdk-go compatabile objects 224 for _, pRaw := range configured { 225 data := pRaw.(map[string]interface{}) 226 227 if data["name"].(string) == "" { 228 continue 229 } 230 231 p := &rds.Parameter{ 232 ApplyMethod: aws.String(data["apply_method"].(string)), 233 ParameterName: aws.String(data["name"].(string)), 234 ParameterValue: aws.String(data["value"].(string)), 235 } 236 237 parameters = append(parameters, p) 238 } 239 240 return parameters, nil 241 } 242 243 func expandRedshiftParameters(configured []interface{}) ([]*redshift.Parameter, error) { 244 var parameters []*redshift.Parameter 245 246 // Loop over our configured parameters and create 247 // an array of aws-sdk-go compatabile objects 248 for _, pRaw := range configured { 249 data := pRaw.(map[string]interface{}) 250 251 if data["name"].(string) == "" { 252 continue 253 } 254 255 p := &redshift.Parameter{ 256 ParameterName: aws.String(data["name"].(string)), 257 ParameterValue: aws.String(data["value"].(string)), 258 } 259 260 parameters = append(parameters, p) 261 } 262 263 return parameters, nil 264 } 265 266 func expandOptionConfiguration(configured []interface{}) ([]*rds.OptionConfiguration, error) { 267 var option []*rds.OptionConfiguration 268 269 for _, pRaw := range configured { 270 data := pRaw.(map[string]interface{}) 271 272 o := &rds.OptionConfiguration{ 273 OptionName: aws.String(data["option_name"].(string)), 274 } 275 276 if raw, ok := data["port"]; ok { 277 port := raw.(int) 278 if port != 0 { 279 o.Port = aws.Int64(int64(port)) 280 } 281 } 282 283 if raw, ok := data["db_security_group_memberships"]; ok { 284 memberships := expandStringList(raw.(*schema.Set).List()) 285 if len(memberships) > 0 { 286 o.DBSecurityGroupMemberships = memberships 287 } 288 } 289 290 if raw, ok := data["vpc_security_group_memberships"]; ok { 291 memberships := expandStringList(raw.(*schema.Set).List()) 292 if len(memberships) > 0 { 293 o.VpcSecurityGroupMemberships = memberships 294 } 295 } 296 297 if raw, ok := data["option_settings"]; ok { 298 o.OptionSettings = expandOptionSetting(raw.(*schema.Set).List()) 299 } 300 301 option = append(option, o) 302 } 303 304 return option, nil 305 } 306 307 func expandOptionSetting(list []interface{}) []*rds.OptionSetting { 308 options := make([]*rds.OptionSetting, 0, len(list)) 309 310 for _, oRaw := range list { 311 data := oRaw.(map[string]interface{}) 312 313 o := &rds.OptionSetting{ 314 Name: aws.String(data["name"].(string)), 315 Value: aws.String(data["value"].(string)), 316 } 317 318 options = append(options, o) 319 } 320 321 return options 322 } 323 324 // Takes the result of flatmap.Expand for an array of parameters and 325 // returns Parameter API compatible objects 326 func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.ParameterNameValue, error) { 327 parameters := make([]*elasticache.ParameterNameValue, 0, len(configured)) 328 329 // Loop over our configured parameters and create 330 // an array of aws-sdk-go compatabile objects 331 for _, pRaw := range configured { 332 data := pRaw.(map[string]interface{}) 333 334 p := &elasticache.ParameterNameValue{ 335 ParameterName: aws.String(data["name"].(string)), 336 ParameterValue: aws.String(data["value"].(string)), 337 } 338 339 parameters = append(parameters, p) 340 } 341 342 return parameters, nil 343 } 344 345 // Flattens an access log into something that flatmap.Flatten() can handle 346 func flattenAccessLog(l *elb.AccessLog) []map[string]interface{} { 347 result := make([]map[string]interface{}, 0, 1) 348 349 if l != nil && *l.Enabled { 350 r := make(map[string]interface{}) 351 if l.S3BucketName != nil { 352 r["bucket"] = *l.S3BucketName 353 } 354 355 if l.S3BucketPrefix != nil { 356 r["bucket_prefix"] = *l.S3BucketPrefix 357 } 358 359 if l.EmitInterval != nil { 360 r["interval"] = *l.EmitInterval 361 } 362 363 result = append(result, r) 364 } 365 366 return result 367 } 368 369 // Takes the result of flatmap.Expand for an array of step adjustments and 370 // returns a []*autoscaling.StepAdjustment. 371 func expandStepAdjustments(configured []interface{}) ([]*autoscaling.StepAdjustment, error) { 372 var adjustments []*autoscaling.StepAdjustment 373 374 // Loop over our configured step adjustments and create an array 375 // of aws-sdk-go compatible objects. We're forced to convert strings 376 // to floats here because there's no way to detect whether or not 377 // an uninitialized, optional schema element is "0.0" deliberately. 378 // With strings, we can test for "", which is definitely an empty 379 // struct value. 380 for _, raw := range configured { 381 data := raw.(map[string]interface{}) 382 a := &autoscaling.StepAdjustment{ 383 ScalingAdjustment: aws.Int64(int64(data["scaling_adjustment"].(int))), 384 } 385 if data["metric_interval_lower_bound"] != "" { 386 bound := data["metric_interval_lower_bound"] 387 switch bound := bound.(type) { 388 case string: 389 f, err := strconv.ParseFloat(bound, 64) 390 if err != nil { 391 return nil, fmt.Errorf( 392 "metric_interval_lower_bound must be a float value represented as a string") 393 } 394 a.MetricIntervalLowerBound = aws.Float64(f) 395 default: 396 return nil, fmt.Errorf( 397 "metric_interval_lower_bound isn't a string. This is a bug. Please file an issue.") 398 } 399 } 400 if data["metric_interval_upper_bound"] != "" { 401 bound := data["metric_interval_upper_bound"] 402 switch bound := bound.(type) { 403 case string: 404 f, err := strconv.ParseFloat(bound, 64) 405 if err != nil { 406 return nil, fmt.Errorf( 407 "metric_interval_upper_bound must be a float value represented as a string") 408 } 409 a.MetricIntervalUpperBound = aws.Float64(f) 410 default: 411 return nil, fmt.Errorf( 412 "metric_interval_upper_bound isn't a string. This is a bug. Please file an issue.") 413 } 414 } 415 adjustments = append(adjustments, a) 416 } 417 418 return adjustments, nil 419 } 420 421 // Flattens a health check into something that flatmap.Flatten() 422 // can handle 423 func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} { 424 result := make([]map[string]interface{}, 0, 1) 425 426 chk := make(map[string]interface{}) 427 chk["unhealthy_threshold"] = *check.UnhealthyThreshold 428 chk["healthy_threshold"] = *check.HealthyThreshold 429 chk["target"] = *check.Target 430 chk["timeout"] = *check.Timeout 431 chk["interval"] = *check.Interval 432 433 result = append(result, chk) 434 435 return result 436 } 437 438 // Flattens an array of UserSecurityGroups into a []*ec2.GroupIdentifier 439 func flattenSecurityGroups(list []*ec2.UserIdGroupPair, ownerId *string) []*ec2.GroupIdentifier { 440 result := make([]*ec2.GroupIdentifier, 0, len(list)) 441 for _, g := range list { 442 var userId *string 443 if g.UserId != nil && *g.UserId != "" && (ownerId == nil || *ownerId != *g.UserId) { 444 userId = g.UserId 445 } 446 // userid nil here for same vpc groups 447 448 vpc := g.GroupName == nil || *g.GroupName == "" 449 var id *string 450 if vpc { 451 id = g.GroupId 452 } else { 453 id = g.GroupName 454 } 455 456 // id is groupid for vpcs 457 // id is groupname for non vpc (classic) 458 459 if userId != nil { 460 id = aws.String(*userId + "/" + *id) 461 } 462 463 if vpc { 464 result = append(result, &ec2.GroupIdentifier{ 465 GroupId: id, 466 }) 467 } else { 468 result = append(result, &ec2.GroupIdentifier{ 469 GroupId: g.GroupId, 470 GroupName: id, 471 }) 472 } 473 } 474 return result 475 } 476 477 // Flattens an array of Instances into a []string 478 func flattenInstances(list []*elb.Instance) []string { 479 result := make([]string, 0, len(list)) 480 for _, i := range list { 481 result = append(result, *i.InstanceId) 482 } 483 return result 484 } 485 486 // Expands an array of String Instance IDs into a []Instances 487 func expandInstanceString(list []interface{}) []*elb.Instance { 488 result := make([]*elb.Instance, 0, len(list)) 489 for _, i := range list { 490 result = append(result, &elb.Instance{InstanceId: aws.String(i.(string))}) 491 } 492 return result 493 } 494 495 // Flattens an array of Backend Descriptions into a a map of instance_port to policy names. 496 func flattenBackendPolicies(backends []*elb.BackendServerDescription) map[int64][]string { 497 policies := make(map[int64][]string) 498 for _, i := range backends { 499 for _, p := range i.PolicyNames { 500 policies[*i.InstancePort] = append(policies[*i.InstancePort], *p) 501 } 502 sort.Strings(policies[*i.InstancePort]) 503 } 504 return policies 505 } 506 507 // Flattens an array of Listeners into a []map[string]interface{} 508 func flattenListeners(list []*elb.ListenerDescription) []map[string]interface{} { 509 result := make([]map[string]interface{}, 0, len(list)) 510 for _, i := range list { 511 l := map[string]interface{}{ 512 "instance_port": *i.Listener.InstancePort, 513 "instance_protocol": strings.ToLower(*i.Listener.InstanceProtocol), 514 "lb_port": *i.Listener.LoadBalancerPort, 515 "lb_protocol": strings.ToLower(*i.Listener.Protocol), 516 } 517 // SSLCertificateID is optional, and may be nil 518 if i.Listener.SSLCertificateId != nil { 519 l["ssl_certificate_id"] = *i.Listener.SSLCertificateId 520 } 521 result = append(result, l) 522 } 523 return result 524 } 525 526 // Flattens an array of Volumes into a []map[string]interface{} 527 func flattenEcsVolumes(list []*ecs.Volume) []map[string]interface{} { 528 result := make([]map[string]interface{}, 0, len(list)) 529 for _, volume := range list { 530 l := map[string]interface{}{ 531 "name": *volume.Name, 532 } 533 534 if volume.Host.SourcePath != nil { 535 l["host_path"] = *volume.Host.SourcePath 536 } 537 538 result = append(result, l) 539 } 540 return result 541 } 542 543 // Flattens an array of ECS LoadBalancers into a []map[string]interface{} 544 func flattenEcsLoadBalancers(list []*ecs.LoadBalancer) []map[string]interface{} { 545 result := make([]map[string]interface{}, 0, len(list)) 546 for _, loadBalancer := range list { 547 l := map[string]interface{}{ 548 "elb_name": *loadBalancer.LoadBalancerName, 549 "container_name": *loadBalancer.ContainerName, 550 "container_port": *loadBalancer.ContainerPort, 551 } 552 result = append(result, l) 553 } 554 return result 555 } 556 557 // Encodes an array of ecs.ContainerDefinitions into a JSON string 558 func flattenEcsContainerDefinitions(definitions []*ecs.ContainerDefinition) (string, error) { 559 byteArray, err := json.Marshal(definitions) 560 if err != nil { 561 return "", fmt.Errorf("Error encoding to JSON: %s", err) 562 } 563 564 n := bytes.Index(byteArray, []byte{0}) 565 return string(byteArray[:n]), nil 566 } 567 568 // Flattens an array of Options into a []map[string]interface{} 569 func flattenOptions(list []*rds.Option) []map[string]interface{} { 570 result := make([]map[string]interface{}, 0, len(list)) 571 for _, i := range list { 572 if i.OptionName != nil { 573 r := make(map[string]interface{}) 574 r["option_name"] = strings.ToLower(*i.OptionName) 575 // Default empty string, guard against nil parameter values 576 r["port"] = "" 577 if i.Port != nil { 578 r["port"] = int(*i.Port) 579 } 580 if i.VpcSecurityGroupMemberships != nil { 581 vpcs := make([]string, 0, len(i.VpcSecurityGroupMemberships)) 582 for _, vpc := range i.VpcSecurityGroupMemberships { 583 id := vpc.VpcSecurityGroupId 584 vpcs = append(vpcs, *id) 585 } 586 587 r["vpc_security_group_memberships"] = vpcs 588 } 589 if i.DBSecurityGroupMemberships != nil { 590 dbs := make([]string, 0, len(i.DBSecurityGroupMemberships)) 591 for _, db := range i.DBSecurityGroupMemberships { 592 id := db.DBSecurityGroupName 593 dbs = append(dbs, *id) 594 } 595 596 r["db_security_group_memberships"] = dbs 597 } 598 if i.OptionSettings != nil { 599 settings := make([]map[string]interface{}, 0, len(i.OptionSettings)) 600 for _, j := range i.OptionSettings { 601 settings = append(settings, map[string]interface{}{ 602 "name": *j.Name, 603 "value": *j.Value, 604 }) 605 } 606 607 r["option_settings"] = settings 608 } 609 result = append(result, r) 610 } 611 } 612 return result 613 } 614 615 // Flattens an array of Parameters into a []map[string]interface{} 616 func flattenParameters(list []*rds.Parameter) []map[string]interface{} { 617 result := make([]map[string]interface{}, 0, len(list)) 618 for _, i := range list { 619 if i.ParameterName != nil { 620 r := make(map[string]interface{}) 621 r["name"] = strings.ToLower(*i.ParameterName) 622 // Default empty string, guard against nil parameter values 623 r["value"] = "" 624 if i.ParameterValue != nil { 625 r["value"] = strings.ToLower(*i.ParameterValue) 626 } 627 result = append(result, r) 628 } 629 } 630 return result 631 } 632 633 // Flattens an array of Redshift Parameters into a []map[string]interface{} 634 func flattenRedshiftParameters(list []*redshift.Parameter) []map[string]interface{} { 635 result := make([]map[string]interface{}, 0, len(list)) 636 for _, i := range list { 637 result = append(result, map[string]interface{}{ 638 "name": strings.ToLower(*i.ParameterName), 639 "value": strings.ToLower(*i.ParameterValue), 640 }) 641 } 642 return result 643 } 644 645 // Flattens an array of Parameters into a []map[string]interface{} 646 func flattenElastiCacheParameters(list []*elasticache.Parameter) []map[string]interface{} { 647 result := make([]map[string]interface{}, 0, len(list)) 648 for _, i := range list { 649 if i.ParameterValue != nil { 650 result = append(result, map[string]interface{}{ 651 "name": strings.ToLower(*i.ParameterName), 652 "value": strings.ToLower(*i.ParameterValue), 653 }) 654 } 655 } 656 return result 657 } 658 659 // Takes the result of flatmap.Expand for an array of strings 660 // and returns a []*string 661 func expandStringList(configured []interface{}) []*string { 662 vs := make([]*string, 0, len(configured)) 663 for _, v := range configured { 664 vs = append(vs, aws.String(v.(string))) 665 } 666 return vs 667 } 668 669 // Takes the result of schema.Set of strings and returns a []*string 670 func expandStringSet(configured *schema.Set) []*string { 671 return expandStringList(configured.List()) 672 } 673 674 // Takes list of pointers to strings. Expand to an array 675 // of raw strings and returns a []interface{} 676 // to keep compatibility w/ schema.NewSetschema.NewSet 677 func flattenStringList(list []*string) []interface{} { 678 vs := make([]interface{}, 0, len(list)) 679 for _, v := range list { 680 vs = append(vs, *v) 681 } 682 return vs 683 } 684 685 //Flattens an array of private ip addresses into a []string, where the elements returned are the IP strings e.g. "192.168.0.0" 686 func flattenNetworkInterfacesPrivateIPAddresses(dtos []*ec2.NetworkInterfacePrivateIpAddress) []string { 687 ips := make([]string, 0, len(dtos)) 688 for _, v := range dtos { 689 ip := *v.PrivateIpAddress 690 ips = append(ips, ip) 691 } 692 return ips 693 } 694 695 //Flattens security group identifiers into a []string, where the elements returned are the GroupIDs 696 func flattenGroupIdentifiers(dtos []*ec2.GroupIdentifier) []string { 697 ids := make([]string, 0, len(dtos)) 698 for _, v := range dtos { 699 group_id := *v.GroupId 700 ids = append(ids, group_id) 701 } 702 return ids 703 } 704 705 //Expands an array of IPs into a ec2 Private IP Address Spec 706 func expandPrivateIPAddresses(ips []interface{}) []*ec2.PrivateIpAddressSpecification { 707 dtos := make([]*ec2.PrivateIpAddressSpecification, 0, len(ips)) 708 for i, v := range ips { 709 new_private_ip := &ec2.PrivateIpAddressSpecification{ 710 PrivateIpAddress: aws.String(v.(string)), 711 } 712 713 new_private_ip.Primary = aws.Bool(i == 0) 714 715 dtos = append(dtos, new_private_ip) 716 } 717 return dtos 718 } 719 720 //Flattens network interface attachment into a map[string]interface 721 func flattenAttachment(a *ec2.NetworkInterfaceAttachment) map[string]interface{} { 722 att := make(map[string]interface{}) 723 if a.InstanceId != nil { 724 att["instance"] = *a.InstanceId 725 } 726 att["device_index"] = *a.DeviceIndex 727 att["attachment_id"] = *a.AttachmentId 728 return att 729 } 730 731 // Flattens step adjustments into a list of map[string]interface. 732 func flattenStepAdjustments(adjustments []*autoscaling.StepAdjustment) []map[string]interface{} { 733 result := make([]map[string]interface{}, 0, len(adjustments)) 734 for _, raw := range adjustments { 735 a := map[string]interface{}{ 736 "scaling_adjustment": *raw.ScalingAdjustment, 737 } 738 if raw.MetricIntervalUpperBound != nil { 739 a["metric_interval_upper_bound"] = *raw.MetricIntervalUpperBound 740 } 741 if raw.MetricIntervalLowerBound != nil { 742 a["metric_interval_lower_bound"] = *raw.MetricIntervalLowerBound 743 } 744 result = append(result, a) 745 } 746 return result 747 } 748 749 func flattenResourceRecords(recs []*route53.ResourceRecord) []string { 750 strs := make([]string, 0, len(recs)) 751 for _, r := range recs { 752 if r.Value != nil { 753 s := strings.Replace(*r.Value, "\"", "", 2) 754 strs = append(strs, s) 755 } 756 } 757 return strs 758 } 759 760 func expandResourceRecords(recs []interface{}, typeStr string) []*route53.ResourceRecord { 761 records := make([]*route53.ResourceRecord, 0, len(recs)) 762 for _, r := range recs { 763 s := r.(string) 764 switch typeStr { 765 case "TXT", "SPF": 766 str := fmt.Sprintf("\"%s\"", s) 767 records = append(records, &route53.ResourceRecord{Value: aws.String(str)}) 768 default: 769 records = append(records, &route53.ResourceRecord{Value: aws.String(s)}) 770 } 771 } 772 return records 773 } 774 775 func expandESClusterConfig(m map[string]interface{}) *elasticsearch.ElasticsearchClusterConfig { 776 config := elasticsearch.ElasticsearchClusterConfig{} 777 778 if v, ok := m["dedicated_master_enabled"]; ok { 779 isEnabled := v.(bool) 780 config.DedicatedMasterEnabled = aws.Bool(isEnabled) 781 782 if isEnabled { 783 if v, ok := m["dedicated_master_count"]; ok && v.(int) > 0 { 784 config.DedicatedMasterCount = aws.Int64(int64(v.(int))) 785 } 786 if v, ok := m["dedicated_master_type"]; ok && v.(string) != "" { 787 config.DedicatedMasterType = aws.String(v.(string)) 788 } 789 } 790 } 791 792 if v, ok := m["instance_count"]; ok { 793 config.InstanceCount = aws.Int64(int64(v.(int))) 794 } 795 if v, ok := m["instance_type"]; ok { 796 config.InstanceType = aws.String(v.(string)) 797 } 798 799 if v, ok := m["zone_awareness_enabled"]; ok { 800 config.ZoneAwarenessEnabled = aws.Bool(v.(bool)) 801 } 802 803 return &config 804 } 805 806 func flattenESClusterConfig(c *elasticsearch.ElasticsearchClusterConfig) []map[string]interface{} { 807 m := map[string]interface{}{} 808 809 if c.DedicatedMasterCount != nil { 810 m["dedicated_master_count"] = *c.DedicatedMasterCount 811 } 812 if c.DedicatedMasterEnabled != nil { 813 m["dedicated_master_enabled"] = *c.DedicatedMasterEnabled 814 } 815 if c.DedicatedMasterType != nil { 816 m["dedicated_master_type"] = *c.DedicatedMasterType 817 } 818 if c.InstanceCount != nil { 819 m["instance_count"] = *c.InstanceCount 820 } 821 if c.InstanceType != nil { 822 m["instance_type"] = *c.InstanceType 823 } 824 if c.ZoneAwarenessEnabled != nil { 825 m["zone_awareness_enabled"] = *c.ZoneAwarenessEnabled 826 } 827 828 return []map[string]interface{}{m} 829 } 830 831 func flattenESEBSOptions(o *elasticsearch.EBSOptions) []map[string]interface{} { 832 m := map[string]interface{}{} 833 834 if o.EBSEnabled != nil { 835 m["ebs_enabled"] = *o.EBSEnabled 836 } 837 if o.Iops != nil { 838 m["iops"] = *o.Iops 839 } 840 if o.VolumeSize != nil { 841 m["volume_size"] = *o.VolumeSize 842 } 843 if o.VolumeType != nil { 844 m["volume_type"] = *o.VolumeType 845 } 846 847 return []map[string]interface{}{m} 848 } 849 850 func expandESEBSOptions(m map[string]interface{}) *elasticsearch.EBSOptions { 851 options := elasticsearch.EBSOptions{} 852 853 if v, ok := m["ebs_enabled"]; ok { 854 options.EBSEnabled = aws.Bool(v.(bool)) 855 } 856 if v, ok := m["iops"]; ok && v.(int) > 0 { 857 options.Iops = aws.Int64(int64(v.(int))) 858 } 859 if v, ok := m["volume_size"]; ok && v.(int) > 0 { 860 options.VolumeSize = aws.Int64(int64(v.(int))) 861 } 862 if v, ok := m["volume_type"]; ok && v.(string) != "" { 863 options.VolumeType = aws.String(v.(string)) 864 } 865 866 return &options 867 } 868 869 func pointersMapToStringList(pointers map[string]*string) map[string]interface{} { 870 list := make(map[string]interface{}, len(pointers)) 871 for i, v := range pointers { 872 list[i] = *v 873 } 874 return list 875 } 876 877 func stringMapToPointers(m map[string]interface{}) map[string]*string { 878 list := make(map[string]*string, len(m)) 879 for i, v := range m { 880 list[i] = aws.String(v.(string)) 881 } 882 return list 883 } 884 885 func flattenDSVpcSettings( 886 s *directoryservice.DirectoryVpcSettingsDescription) []map[string]interface{} { 887 settings := make(map[string]interface{}, 0) 888 889 if s == nil { 890 return nil 891 } 892 893 settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) 894 settings["vpc_id"] = *s.VpcId 895 896 return []map[string]interface{}{settings} 897 } 898 899 func flattenLambdaVpcConfigResponse(s *lambda.VpcConfigResponse) []map[string]interface{} { 900 settings := make(map[string]interface{}, 0) 901 902 if s == nil { 903 return nil 904 } 905 906 if len(s.SubnetIds) == 0 && len(s.SecurityGroupIds) == 0 && s.VpcId == nil { 907 return nil 908 } 909 910 settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) 911 settings["security_group_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SecurityGroupIds)) 912 if s.VpcId != nil { 913 settings["vpc_id"] = *s.VpcId 914 } 915 916 return []map[string]interface{}{settings} 917 } 918 919 func flattenDSConnectSettings( 920 customerDnsIps []*string, 921 s *directoryservice.DirectoryConnectSettingsDescription) []map[string]interface{} { 922 if s == nil { 923 return nil 924 } 925 926 settings := make(map[string]interface{}, 0) 927 928 settings["customer_dns_ips"] = schema.NewSet(schema.HashString, flattenStringList(customerDnsIps)) 929 settings["connect_ips"] = schema.NewSet(schema.HashString, flattenStringList(s.ConnectIps)) 930 settings["customer_username"] = *s.CustomerUserName 931 settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) 932 settings["vpc_id"] = *s.VpcId 933 934 return []map[string]interface{}{settings} 935 } 936 937 func expandCloudFormationParameters(params map[string]interface{}) []*cloudformation.Parameter { 938 var cfParams []*cloudformation.Parameter 939 for k, v := range params { 940 cfParams = append(cfParams, &cloudformation.Parameter{ 941 ParameterKey: aws.String(k), 942 ParameterValue: aws.String(v.(string)), 943 }) 944 } 945 946 return cfParams 947 } 948 949 // flattenCloudFormationParameters is flattening list of 950 // *cloudformation.Parameters and only returning existing 951 // parameters to avoid clash with default values 952 func flattenCloudFormationParameters(cfParams []*cloudformation.Parameter, 953 originalParams map[string]interface{}) map[string]interface{} { 954 params := make(map[string]interface{}, len(cfParams)) 955 for _, p := range cfParams { 956 _, isConfigured := originalParams[*p.ParameterKey] 957 if isConfigured { 958 params[*p.ParameterKey] = *p.ParameterValue 959 } 960 } 961 return params 962 } 963 964 func expandCloudFormationTags(tags map[string]interface{}) []*cloudformation.Tag { 965 var cfTags []*cloudformation.Tag 966 for k, v := range tags { 967 cfTags = append(cfTags, &cloudformation.Tag{ 968 Key: aws.String(k), 969 Value: aws.String(v.(string)), 970 }) 971 } 972 return cfTags 973 } 974 975 func flattenCloudFormationTags(cfTags []*cloudformation.Tag) map[string]string { 976 tags := make(map[string]string, len(cfTags)) 977 for _, t := range cfTags { 978 tags[*t.Key] = *t.Value 979 } 980 return tags 981 } 982 983 func flattenCloudFormationOutputs(cfOutputs []*cloudformation.Output) map[string]string { 984 outputs := make(map[string]string, len(cfOutputs)) 985 for _, o := range cfOutputs { 986 outputs[*o.OutputKey] = *o.OutputValue 987 } 988 return outputs 989 } 990 991 func flattenAsgEnabledMetrics(list []*autoscaling.EnabledMetric) []string { 992 strs := make([]string, 0, len(list)) 993 for _, r := range list { 994 if r.Metric != nil { 995 strs = append(strs, *r.Metric) 996 } 997 } 998 return strs 999 } 1000 1001 func expandApiGatewayStageKeys(d *schema.ResourceData) []*apigateway.StageKey { 1002 var stageKeys []*apigateway.StageKey 1003 1004 if stageKeyData, ok := d.GetOk("stage_key"); ok { 1005 params := stageKeyData.(*schema.Set).List() 1006 for k := range params { 1007 data := params[k].(map[string]interface{}) 1008 stageKeys = append(stageKeys, &apigateway.StageKey{ 1009 RestApiId: aws.String(data["rest_api_id"].(string)), 1010 StageName: aws.String(data["stage_name"].(string)), 1011 }) 1012 } 1013 } 1014 1015 return stageKeys 1016 } 1017 1018 func expandApiGatewayRequestResponseModelOperations(d *schema.ResourceData, key string, prefix string) []*apigateway.PatchOperation { 1019 operations := make([]*apigateway.PatchOperation, 0) 1020 1021 oldModels, newModels := d.GetChange(key) 1022 oldModelMap := oldModels.(map[string]interface{}) 1023 newModelMap := newModels.(map[string]interface{}) 1024 1025 for k, _ := range oldModelMap { 1026 operation := apigateway.PatchOperation{ 1027 Op: aws.String("remove"), 1028 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))), 1029 } 1030 1031 for nK, nV := range newModelMap { 1032 if nK == k { 1033 operation.Op = aws.String("replace") 1034 operation.Value = aws.String(nV.(string)) 1035 } 1036 } 1037 1038 operations = append(operations, &operation) 1039 } 1040 1041 for nK, nV := range newModelMap { 1042 exists := false 1043 for k, _ := range oldModelMap { 1044 if k == nK { 1045 exists = true 1046 } 1047 } 1048 if !exists { 1049 operation := apigateway.PatchOperation{ 1050 Op: aws.String("add"), 1051 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(nK, "/", "~1", -1))), 1052 Value: aws.String(nV.(string)), 1053 } 1054 operations = append(operations, &operation) 1055 } 1056 } 1057 1058 return operations 1059 } 1060 1061 func expandApiGatewayMethodParametersJSONOperations(d *schema.ResourceData, key string, prefix string) ([]*apigateway.PatchOperation, error) { 1062 operations := make([]*apigateway.PatchOperation, 0) 1063 1064 oldParameters, newParameters := d.GetChange(key) 1065 oldParametersMap := make(map[string]interface{}) 1066 newParametersMap := make(map[string]interface{}) 1067 1068 if err := json.Unmarshal([]byte(oldParameters.(string)), &oldParametersMap); err != nil { 1069 err := fmt.Errorf("Error unmarshaling old %s: %s", key, err) 1070 return operations, err 1071 } 1072 1073 if err := json.Unmarshal([]byte(newParameters.(string)), &newParametersMap); err != nil { 1074 err := fmt.Errorf("Error unmarshaling new %s: %s", key, err) 1075 return operations, err 1076 } 1077 1078 for k, _ := range oldParametersMap { 1079 operation := apigateway.PatchOperation{ 1080 Op: aws.String("remove"), 1081 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, k)), 1082 } 1083 1084 for nK, nV := range newParametersMap { 1085 if nK == k { 1086 operation.Op = aws.String("replace") 1087 operation.Value = aws.String(strconv.FormatBool(nV.(bool))) 1088 } 1089 } 1090 1091 operations = append(operations, &operation) 1092 } 1093 1094 for nK, nV := range newParametersMap { 1095 exists := false 1096 for k, _ := range oldParametersMap { 1097 if k == nK { 1098 exists = true 1099 } 1100 } 1101 if !exists { 1102 operation := apigateway.PatchOperation{ 1103 Op: aws.String("add"), 1104 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, nK)), 1105 Value: aws.String(strconv.FormatBool(nV.(bool))), 1106 } 1107 operations = append(operations, &operation) 1108 } 1109 } 1110 1111 return operations, nil 1112 } 1113 1114 func expandApiGatewayStageKeyOperations(d *schema.ResourceData) []*apigateway.PatchOperation { 1115 operations := make([]*apigateway.PatchOperation, 0) 1116 1117 prev, curr := d.GetChange("stage_key") 1118 prevList := prev.(*schema.Set).List() 1119 currList := curr.(*schema.Set).List() 1120 1121 for i := range prevList { 1122 p := prevList[i].(map[string]interface{}) 1123 exists := false 1124 1125 for j := range currList { 1126 c := currList[j].(map[string]interface{}) 1127 if c["rest_api_id"].(string) == p["rest_api_id"].(string) && c["stage_name"].(string) == p["stage_name"].(string) { 1128 exists = true 1129 } 1130 } 1131 1132 if !exists { 1133 operations = append(operations, &apigateway.PatchOperation{ 1134 Op: aws.String("remove"), 1135 Path: aws.String("/stages"), 1136 Value: aws.String(fmt.Sprintf("%s/%s", p["rest_api_id"].(string), p["stage_name"].(string))), 1137 }) 1138 } 1139 } 1140 1141 for i := range currList { 1142 c := currList[i].(map[string]interface{}) 1143 exists := false 1144 1145 for j := range prevList { 1146 p := prevList[j].(map[string]interface{}) 1147 if c["rest_api_id"].(string) == p["rest_api_id"].(string) && c["stage_name"].(string) == p["stage_name"].(string) { 1148 exists = true 1149 } 1150 } 1151 1152 if !exists { 1153 operations = append(operations, &apigateway.PatchOperation{ 1154 Op: aws.String("add"), 1155 Path: aws.String("/stages"), 1156 Value: aws.String(fmt.Sprintf("%s/%s", c["rest_api_id"].(string), c["stage_name"].(string))), 1157 }) 1158 } 1159 } 1160 1161 return operations 1162 } 1163 1164 func expandCloudWachLogMetricTransformations(m map[string]interface{}) []*cloudwatchlogs.MetricTransformation { 1165 transformation := cloudwatchlogs.MetricTransformation{ 1166 MetricName: aws.String(m["name"].(string)), 1167 MetricNamespace: aws.String(m["namespace"].(string)), 1168 MetricValue: aws.String(m["value"].(string)), 1169 } 1170 1171 return []*cloudwatchlogs.MetricTransformation{&transformation} 1172 } 1173 1174 func flattenCloudWachLogMetricTransformations(ts []*cloudwatchlogs.MetricTransformation) map[string]string { 1175 m := make(map[string]string, 0) 1176 1177 m["name"] = *ts[0].MetricName 1178 m["namespace"] = *ts[0].MetricNamespace 1179 m["value"] = *ts[0].MetricValue 1180 1181 return m 1182 } 1183 1184 func flattenBeanstalkAsg(list []*elasticbeanstalk.AutoScalingGroup) []string { 1185 strs := make([]string, 0, len(list)) 1186 for _, r := range list { 1187 if r.Name != nil { 1188 strs = append(strs, *r.Name) 1189 } 1190 } 1191 return strs 1192 } 1193 1194 func flattenBeanstalkInstances(list []*elasticbeanstalk.Instance) []string { 1195 strs := make([]string, 0, len(list)) 1196 for _, r := range list { 1197 if r.Id != nil { 1198 strs = append(strs, *r.Id) 1199 } 1200 } 1201 return strs 1202 } 1203 1204 func flattenBeanstalkLc(list []*elasticbeanstalk.LaunchConfiguration) []string { 1205 strs := make([]string, 0, len(list)) 1206 for _, r := range list { 1207 if r.Name != nil { 1208 strs = append(strs, *r.Name) 1209 } 1210 } 1211 return strs 1212 } 1213 1214 func flattenBeanstalkElb(list []*elasticbeanstalk.LoadBalancer) []string { 1215 strs := make([]string, 0, len(list)) 1216 for _, r := range list { 1217 if r.Name != nil { 1218 strs = append(strs, *r.Name) 1219 } 1220 } 1221 return strs 1222 } 1223 1224 func flattenBeanstalkSqs(list []*elasticbeanstalk.Queue) []string { 1225 strs := make([]string, 0, len(list)) 1226 for _, r := range list { 1227 if r.URL != nil { 1228 strs = append(strs, *r.URL) 1229 } 1230 } 1231 return strs 1232 } 1233 1234 func flattenBeanstalkTrigger(list []*elasticbeanstalk.Trigger) []string { 1235 strs := make([]string, 0, len(list)) 1236 for _, r := range list { 1237 if r.Name != nil { 1238 strs = append(strs, *r.Name) 1239 } 1240 } 1241 return strs 1242 } 1243 1244 // There are several parts of the AWS API that will sort lists of strings, 1245 // causing diffs inbetweeen resources that use lists. This avoids a bit of 1246 // code duplication for pre-sorts that can be used for things like hash 1247 // functions, etc. 1248 func sortInterfaceSlice(in []interface{}) []interface{} { 1249 a := []string{} 1250 b := []interface{}{} 1251 for _, v := range in { 1252 a = append(a, v.(string)) 1253 } 1254 1255 sort.Strings(a) 1256 1257 for _, v := range a { 1258 b = append(b, v) 1259 } 1260 1261 return b 1262 } 1263 1264 func flattenApiGatewayThrottleSettings(settings *apigateway.ThrottleSettings) []map[string]interface{} { 1265 result := make([]map[string]interface{}, 0, 1) 1266 1267 if settings != nil { 1268 r := make(map[string]interface{}) 1269 if settings.BurstLimit != nil { 1270 r["burst_limit"] = *settings.BurstLimit 1271 } 1272 1273 if settings.RateLimit != nil { 1274 r["rate_limit"] = *settings.RateLimit 1275 } 1276 1277 result = append(result, r) 1278 } 1279 1280 return result 1281 } 1282 1283 // TODO: refactor some of these helper functions and types in the terraform/helper packages 1284 1285 // getStringPtr returns a *string version of the value taken from m, where m 1286 // can be a map[string]interface{} or a *schema.ResourceData. If the key isn't 1287 // present or is empty, getNilString returns nil. 1288 func getStringPtr(m interface{}, key string) *string { 1289 switch m := m.(type) { 1290 case map[string]interface{}: 1291 v := m[key] 1292 1293 if v == nil { 1294 return nil 1295 } 1296 1297 s := v.(string) 1298 if s == "" { 1299 return nil 1300 } 1301 1302 return &s 1303 1304 case *schema.ResourceData: 1305 if v, ok := m.GetOk(key); ok { 1306 if v == nil || v.(string) == "" { 1307 return nil 1308 } 1309 s := v.(string) 1310 return &s 1311 } 1312 1313 default: 1314 panic("unknown type in getStringPtr") 1315 } 1316 1317 return nil 1318 } 1319 1320 // getStringPtrList returns a []*string version of the map value. If the key 1321 // isn't present, getNilStringList returns nil. 1322 func getStringPtrList(m map[string]interface{}, key string) []*string { 1323 if v, ok := m[key]; ok { 1324 var stringList []*string 1325 for _, i := range v.([]interface{}) { 1326 s := i.(string) 1327 stringList = append(stringList, &s) 1328 } 1329 1330 return stringList 1331 } 1332 1333 return nil 1334 } 1335 1336 // a convenience wrapper type for the schema.Set map[string]interface{} 1337 // Set operations only alter the underlying map if the value is not nil 1338 type setMap map[string]interface{} 1339 1340 // SetString sets m[key] = *value only if `value != nil` 1341 func (s setMap) SetString(key string, value *string) { 1342 if value == nil { 1343 return 1344 } 1345 1346 s[key] = *value 1347 } 1348 1349 // SetStringMap sets key to value as a map[string]interface{}, stripping any nil 1350 // values. The value parameter can be a map[string]interface{}, a 1351 // map[string]*string, or a map[string]string. 1352 func (s setMap) SetStringMap(key string, value interface{}) { 1353 // because these methods are meant to be chained without intermediate 1354 // checks for nil, we are likely to get interfaces with dynamic types but 1355 // a nil value. 1356 if reflect.ValueOf(value).IsNil() { 1357 return 1358 } 1359 1360 m := make(map[string]interface{}) 1361 1362 switch value := value.(type) { 1363 case map[string]string: 1364 for k, v := range value { 1365 m[k] = v 1366 } 1367 case map[string]*string: 1368 for k, v := range value { 1369 if v == nil { 1370 continue 1371 } 1372 m[k] = *v 1373 } 1374 case map[string]interface{}: 1375 for k, v := range value { 1376 if v == nil { 1377 continue 1378 } 1379 1380 switch v := v.(type) { 1381 case string: 1382 m[k] = v 1383 case *string: 1384 if v != nil { 1385 m[k] = *v 1386 } 1387 default: 1388 panic(fmt.Sprintf("unknown type for SetString: %T", v)) 1389 } 1390 } 1391 } 1392 1393 // catch the case where the interface wasn't nil, but we had no non-nil values 1394 if len(m) > 0 { 1395 s[key] = m 1396 } 1397 } 1398 1399 // Set assigns value to s[key] if value isn't nil 1400 func (s setMap) Set(key string, value interface{}) { 1401 if reflect.ValueOf(value).IsNil() { 1402 return 1403 } 1404 1405 s[key] = value 1406 } 1407 1408 // Map returns the raw map type for a shorter type conversion 1409 func (s setMap) Map() map[string]interface{} { 1410 return map[string]interface{}(s) 1411 } 1412 1413 // MapList returns the map[string]interface{} as a single element in a slice to 1414 // match the schema.Set data type used for structs. 1415 func (s setMap) MapList() []map[string]interface{} { 1416 return []map[string]interface{}{s.Map()} 1417 }