github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/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/cognitoidentity" 18 "github.com/aws/aws-sdk-go/service/configservice" 19 "github.com/aws/aws-sdk-go/service/directoryservice" 20 "github.com/aws/aws-sdk-go/service/ec2" 21 "github.com/aws/aws-sdk-go/service/ecs" 22 "github.com/aws/aws-sdk-go/service/elasticache" 23 "github.com/aws/aws-sdk-go/service/elasticbeanstalk" 24 elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice" 25 "github.com/aws/aws-sdk-go/service/elb" 26 "github.com/aws/aws-sdk-go/service/kinesis" 27 "github.com/aws/aws-sdk-go/service/lambda" 28 "github.com/aws/aws-sdk-go/service/rds" 29 "github.com/aws/aws-sdk-go/service/redshift" 30 "github.com/aws/aws-sdk-go/service/route53" 31 "github.com/hashicorp/terraform/helper/schema" 32 "gopkg.in/yaml.v2" 33 ) 34 35 // Takes the result of flatmap.Expand for an array of listeners and 36 // returns ELB API compatible objects 37 func expandListeners(configured []interface{}) ([]*elb.Listener, error) { 38 listeners := make([]*elb.Listener, 0, len(configured)) 39 40 // Loop over our configured listeners and create 41 // an array of aws-sdk-go compatible objects 42 for _, lRaw := range configured { 43 data := lRaw.(map[string]interface{}) 44 45 ip := int64(data["instance_port"].(int)) 46 lp := int64(data["lb_port"].(int)) 47 l := &elb.Listener{ 48 InstancePort: &ip, 49 InstanceProtocol: aws.String(data["instance_protocol"].(string)), 50 LoadBalancerPort: &lp, 51 Protocol: aws.String(data["lb_protocol"].(string)), 52 } 53 54 if v, ok := data["ssl_certificate_id"]; ok { 55 l.SSLCertificateId = aws.String(v.(string)) 56 } 57 58 var valid bool 59 if l.SSLCertificateId != nil && *l.SSLCertificateId != "" { 60 // validate the protocol is correct 61 for _, p := range []string{"https", "ssl"} { 62 if (strings.ToLower(*l.InstanceProtocol) == p) || (strings.ToLower(*l.Protocol) == p) { 63 valid = true 64 } 65 } 66 } else { 67 valid = true 68 } 69 70 if valid { 71 listeners = append(listeners, l) 72 } else { 73 return nil, fmt.Errorf("[ERR] ELB Listener: ssl_certificate_id may be set only when protocol is 'https' or 'ssl'") 74 } 75 } 76 77 return listeners, nil 78 } 79 80 // Takes the result of flatmap. Expand for an array of listeners and 81 // returns ECS Volume compatible objects 82 func expandEcsVolumes(configured []interface{}) ([]*ecs.Volume, error) { 83 volumes := make([]*ecs.Volume, 0, len(configured)) 84 85 // Loop over our configured volumes and create 86 // an array of aws-sdk-go compatible objects 87 for _, lRaw := range configured { 88 data := lRaw.(map[string]interface{}) 89 90 l := &ecs.Volume{ 91 Name: aws.String(data["name"].(string)), 92 } 93 94 hostPath := data["host_path"].(string) 95 if hostPath != "" { 96 l.Host = &ecs.HostVolumeProperties{ 97 SourcePath: aws.String(hostPath), 98 } 99 } 100 101 volumes = append(volumes, l) 102 } 103 104 return volumes, nil 105 } 106 107 // Takes JSON in a string. Decodes JSON into 108 // an array of ecs.ContainerDefinition compatible objects 109 func expandEcsContainerDefinitions(rawDefinitions string) ([]*ecs.ContainerDefinition, error) { 110 var definitions []*ecs.ContainerDefinition 111 112 err := json.Unmarshal([]byte(rawDefinitions), &definitions) 113 if err != nil { 114 return nil, fmt.Errorf("Error decoding JSON: %s", err) 115 } 116 117 return definitions, nil 118 } 119 120 // Takes the result of flatmap. Expand for an array of load balancers and 121 // returns ecs.LoadBalancer compatible objects 122 func expandEcsLoadBalancers(configured []interface{}) []*ecs.LoadBalancer { 123 loadBalancers := make([]*ecs.LoadBalancer, 0, len(configured)) 124 125 // Loop over our configured load balancers and create 126 // an array of aws-sdk-go compatible objects 127 for _, lRaw := range configured { 128 data := lRaw.(map[string]interface{}) 129 130 l := &ecs.LoadBalancer{ 131 ContainerName: aws.String(data["container_name"].(string)), 132 ContainerPort: aws.Int64(int64(data["container_port"].(int))), 133 } 134 135 if v, ok := data["elb_name"]; ok && v.(string) != "" { 136 l.LoadBalancerName = aws.String(v.(string)) 137 } 138 if v, ok := data["target_group_arn"]; ok && v.(string) != "" { 139 l.TargetGroupArn = aws.String(v.(string)) 140 } 141 142 loadBalancers = append(loadBalancers, l) 143 } 144 145 return loadBalancers 146 } 147 148 // Takes the result of flatmap.Expand for an array of ingress/egress security 149 // group rules and returns EC2 API compatible objects. This function will error 150 // if it finds invalid permissions input, namely a protocol of "-1" with either 151 // to_port or from_port set to a non-zero value. 152 func expandIPPerms( 153 group *ec2.SecurityGroup, configured []interface{}) ([]*ec2.IpPermission, error) { 154 vpc := group.VpcId != nil && *group.VpcId != "" 155 156 perms := make([]*ec2.IpPermission, len(configured)) 157 for i, mRaw := range configured { 158 var perm ec2.IpPermission 159 m := mRaw.(map[string]interface{}) 160 161 perm.FromPort = aws.Int64(int64(m["from_port"].(int))) 162 perm.ToPort = aws.Int64(int64(m["to_port"].(int))) 163 perm.IpProtocol = aws.String(m["protocol"].(string)) 164 165 // When protocol is "-1", AWS won't store any ports for the 166 // rule, but also won't error if the user specifies ports other 167 // than '0'. Force the user to make a deliberate '0' port 168 // choice when specifying a "-1" protocol, and tell them about 169 // AWS's behavior in the error message. 170 if *perm.IpProtocol == "-1" && (*perm.FromPort != 0 || *perm.ToPort != 0) { 171 return nil, fmt.Errorf( 172 "from_port (%d) and to_port (%d) must both be 0 to use the 'ALL' \"-1\" protocol!", 173 *perm.FromPort, *perm.ToPort) 174 } 175 176 var groups []string 177 if raw, ok := m["security_groups"]; ok { 178 list := raw.(*schema.Set).List() 179 for _, v := range list { 180 groups = append(groups, v.(string)) 181 } 182 } 183 if v, ok := m["self"]; ok && v.(bool) { 184 if vpc { 185 groups = append(groups, *group.GroupId) 186 } else { 187 groups = append(groups, *group.GroupName) 188 } 189 } 190 191 if len(groups) > 0 { 192 perm.UserIdGroupPairs = make([]*ec2.UserIdGroupPair, len(groups)) 193 for i, name := range groups { 194 ownerId, id := "", name 195 if items := strings.Split(id, "/"); len(items) > 1 { 196 ownerId, id = items[0], items[1] 197 } 198 199 perm.UserIdGroupPairs[i] = &ec2.UserIdGroupPair{ 200 GroupId: aws.String(id), 201 } 202 203 if ownerId != "" { 204 perm.UserIdGroupPairs[i].UserId = aws.String(ownerId) 205 } 206 207 if !vpc { 208 perm.UserIdGroupPairs[i].GroupId = nil 209 perm.UserIdGroupPairs[i].GroupName = aws.String(id) 210 } 211 } 212 } 213 214 if raw, ok := m["cidr_blocks"]; ok { 215 list := raw.([]interface{}) 216 for _, v := range list { 217 perm.IpRanges = append(perm.IpRanges, &ec2.IpRange{CidrIp: aws.String(v.(string))}) 218 } 219 } 220 if raw, ok := m["ipv6_cidr_blocks"]; ok { 221 list := raw.([]interface{}) 222 for _, v := range list { 223 perm.Ipv6Ranges = append(perm.Ipv6Ranges, &ec2.Ipv6Range{CidrIpv6: aws.String(v.(string))}) 224 } 225 } 226 227 if raw, ok := m["prefix_list_ids"]; ok { 228 list := raw.([]interface{}) 229 for _, v := range list { 230 perm.PrefixListIds = append(perm.PrefixListIds, &ec2.PrefixListId{PrefixListId: aws.String(v.(string))}) 231 } 232 } 233 234 perms[i] = &perm 235 } 236 237 return perms, nil 238 } 239 240 // Takes the result of flatmap.Expand for an array of parameters and 241 // returns Parameter API compatible objects 242 func expandParameters(configured []interface{}) ([]*rds.Parameter, error) { 243 var parameters []*rds.Parameter 244 245 // Loop over our configured parameters and create 246 // an array of aws-sdk-go compatible objects 247 for _, pRaw := range configured { 248 data := pRaw.(map[string]interface{}) 249 250 if data["name"].(string) == "" { 251 continue 252 } 253 254 p := &rds.Parameter{ 255 ApplyMethod: aws.String(data["apply_method"].(string)), 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 expandRedshiftParameters(configured []interface{}) ([]*redshift.Parameter, error) { 267 var parameters []*redshift.Parameter 268 269 // Loop over our configured parameters and create 270 // an array of aws-sdk-go compatible objects 271 for _, pRaw := range configured { 272 data := pRaw.(map[string]interface{}) 273 274 if data["name"].(string) == "" { 275 continue 276 } 277 278 p := &redshift.Parameter{ 279 ParameterName: aws.String(data["name"].(string)), 280 ParameterValue: aws.String(data["value"].(string)), 281 } 282 283 parameters = append(parameters, p) 284 } 285 286 return parameters, nil 287 } 288 289 func expandOptionConfiguration(configured []interface{}) ([]*rds.OptionConfiguration, error) { 290 var option []*rds.OptionConfiguration 291 292 for _, pRaw := range configured { 293 data := pRaw.(map[string]interface{}) 294 295 o := &rds.OptionConfiguration{ 296 OptionName: aws.String(data["option_name"].(string)), 297 } 298 299 if raw, ok := data["port"]; ok { 300 port := raw.(int) 301 if port != 0 { 302 o.Port = aws.Int64(int64(port)) 303 } 304 } 305 306 if raw, ok := data["db_security_group_memberships"]; ok { 307 memberships := expandStringList(raw.(*schema.Set).List()) 308 if len(memberships) > 0 { 309 o.DBSecurityGroupMemberships = memberships 310 } 311 } 312 313 if raw, ok := data["vpc_security_group_memberships"]; ok { 314 memberships := expandStringList(raw.(*schema.Set).List()) 315 if len(memberships) > 0 { 316 o.VpcSecurityGroupMemberships = memberships 317 } 318 } 319 320 if raw, ok := data["option_settings"]; ok { 321 o.OptionSettings = expandOptionSetting(raw.(*schema.Set).List()) 322 } 323 324 option = append(option, o) 325 } 326 327 return option, nil 328 } 329 330 func expandOptionSetting(list []interface{}) []*rds.OptionSetting { 331 options := make([]*rds.OptionSetting, 0, len(list)) 332 333 for _, oRaw := range list { 334 data := oRaw.(map[string]interface{}) 335 336 o := &rds.OptionSetting{ 337 Name: aws.String(data["name"].(string)), 338 Value: aws.String(data["value"].(string)), 339 } 340 341 options = append(options, o) 342 } 343 344 return options 345 } 346 347 // Takes the result of flatmap.Expand for an array of parameters and 348 // returns Parameter API compatible objects 349 func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.ParameterNameValue, error) { 350 parameters := make([]*elasticache.ParameterNameValue, 0, len(configured)) 351 352 // Loop over our configured parameters and create 353 // an array of aws-sdk-go compatible objects 354 for _, pRaw := range configured { 355 data := pRaw.(map[string]interface{}) 356 357 p := &elasticache.ParameterNameValue{ 358 ParameterName: aws.String(data["name"].(string)), 359 ParameterValue: aws.String(data["value"].(string)), 360 } 361 362 parameters = append(parameters, p) 363 } 364 365 return parameters, nil 366 } 367 368 // Flattens an access log into something that flatmap.Flatten() can handle 369 func flattenAccessLog(l *elb.AccessLog) []map[string]interface{} { 370 result := make([]map[string]interface{}, 0, 1) 371 372 if l == nil { 373 return nil 374 } 375 376 r := make(map[string]interface{}) 377 if l.S3BucketName != nil { 378 r["bucket"] = *l.S3BucketName 379 } 380 381 if l.S3BucketPrefix != nil { 382 r["bucket_prefix"] = *l.S3BucketPrefix 383 } 384 385 if l.EmitInterval != nil { 386 r["interval"] = *l.EmitInterval 387 } 388 389 if l.Enabled != nil { 390 r["enabled"] = *l.Enabled 391 } 392 393 result = append(result, r) 394 395 return result 396 } 397 398 // Takes the result of flatmap.Expand for an array of step adjustments and 399 // returns a []*autoscaling.StepAdjustment. 400 func expandStepAdjustments(configured []interface{}) ([]*autoscaling.StepAdjustment, error) { 401 var adjustments []*autoscaling.StepAdjustment 402 403 // Loop over our configured step adjustments and create an array 404 // of aws-sdk-go compatible objects. We're forced to convert strings 405 // to floats here because there's no way to detect whether or not 406 // an uninitialized, optional schema element is "0.0" deliberately. 407 // With strings, we can test for "", which is definitely an empty 408 // struct value. 409 for _, raw := range configured { 410 data := raw.(map[string]interface{}) 411 a := &autoscaling.StepAdjustment{ 412 ScalingAdjustment: aws.Int64(int64(data["scaling_adjustment"].(int))), 413 } 414 if data["metric_interval_lower_bound"] != "" { 415 bound := data["metric_interval_lower_bound"] 416 switch bound := bound.(type) { 417 case string: 418 f, err := strconv.ParseFloat(bound, 64) 419 if err != nil { 420 return nil, fmt.Errorf( 421 "metric_interval_lower_bound must be a float value represented as a string") 422 } 423 a.MetricIntervalLowerBound = aws.Float64(f) 424 default: 425 return nil, fmt.Errorf( 426 "metric_interval_lower_bound isn't a string. This is a bug. Please file an issue.") 427 } 428 } 429 if data["metric_interval_upper_bound"] != "" { 430 bound := data["metric_interval_upper_bound"] 431 switch bound := bound.(type) { 432 case string: 433 f, err := strconv.ParseFloat(bound, 64) 434 if err != nil { 435 return nil, fmt.Errorf( 436 "metric_interval_upper_bound must be a float value represented as a string") 437 } 438 a.MetricIntervalUpperBound = aws.Float64(f) 439 default: 440 return nil, fmt.Errorf( 441 "metric_interval_upper_bound isn't a string. This is a bug. Please file an issue.") 442 } 443 } 444 adjustments = append(adjustments, a) 445 } 446 447 return adjustments, nil 448 } 449 450 // Flattens a health check into something that flatmap.Flatten() 451 // can handle 452 func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} { 453 result := make([]map[string]interface{}, 0, 1) 454 455 chk := make(map[string]interface{}) 456 chk["unhealthy_threshold"] = *check.UnhealthyThreshold 457 chk["healthy_threshold"] = *check.HealthyThreshold 458 chk["target"] = *check.Target 459 chk["timeout"] = *check.Timeout 460 chk["interval"] = *check.Interval 461 462 result = append(result, chk) 463 464 return result 465 } 466 467 // Flattens an array of UserSecurityGroups into a []*ec2.GroupIdentifier 468 func flattenSecurityGroups(list []*ec2.UserIdGroupPair, ownerId *string) []*ec2.GroupIdentifier { 469 result := make([]*ec2.GroupIdentifier, 0, len(list)) 470 for _, g := range list { 471 var userId *string 472 if g.UserId != nil && *g.UserId != "" && (ownerId == nil || *ownerId != *g.UserId) { 473 userId = g.UserId 474 } 475 // userid nil here for same vpc groups 476 477 vpc := g.GroupName == nil || *g.GroupName == "" 478 var id *string 479 if vpc { 480 id = g.GroupId 481 } else { 482 id = g.GroupName 483 } 484 485 // id is groupid for vpcs 486 // id is groupname for non vpc (classic) 487 488 if userId != nil { 489 id = aws.String(*userId + "/" + *id) 490 } 491 492 if vpc { 493 result = append(result, &ec2.GroupIdentifier{ 494 GroupId: id, 495 }) 496 } else { 497 result = append(result, &ec2.GroupIdentifier{ 498 GroupId: g.GroupId, 499 GroupName: id, 500 }) 501 } 502 } 503 return result 504 } 505 506 // Flattens an array of Instances into a []string 507 func flattenInstances(list []*elb.Instance) []string { 508 result := make([]string, 0, len(list)) 509 for _, i := range list { 510 result = append(result, *i.InstanceId) 511 } 512 return result 513 } 514 515 // Expands an array of String Instance IDs into a []Instances 516 func expandInstanceString(list []interface{}) []*elb.Instance { 517 result := make([]*elb.Instance, 0, len(list)) 518 for _, i := range list { 519 result = append(result, &elb.Instance{InstanceId: aws.String(i.(string))}) 520 } 521 return result 522 } 523 524 // Flattens an array of Backend Descriptions into a a map of instance_port to policy names. 525 func flattenBackendPolicies(backends []*elb.BackendServerDescription) map[int64][]string { 526 policies := make(map[int64][]string) 527 for _, i := range backends { 528 for _, p := range i.PolicyNames { 529 policies[*i.InstancePort] = append(policies[*i.InstancePort], *p) 530 } 531 sort.Strings(policies[*i.InstancePort]) 532 } 533 return policies 534 } 535 536 // Flattens an array of Listeners into a []map[string]interface{} 537 func flattenListeners(list []*elb.ListenerDescription) []map[string]interface{} { 538 result := make([]map[string]interface{}, 0, len(list)) 539 for _, i := range list { 540 l := map[string]interface{}{ 541 "instance_port": *i.Listener.InstancePort, 542 "instance_protocol": strings.ToLower(*i.Listener.InstanceProtocol), 543 "lb_port": *i.Listener.LoadBalancerPort, 544 "lb_protocol": strings.ToLower(*i.Listener.Protocol), 545 } 546 // SSLCertificateID is optional, and may be nil 547 if i.Listener.SSLCertificateId != nil { 548 l["ssl_certificate_id"] = *i.Listener.SSLCertificateId 549 } 550 result = append(result, l) 551 } 552 return result 553 } 554 555 // Flattens an array of Volumes into a []map[string]interface{} 556 func flattenEcsVolumes(list []*ecs.Volume) []map[string]interface{} { 557 result := make([]map[string]interface{}, 0, len(list)) 558 for _, volume := range list { 559 l := map[string]interface{}{ 560 "name": *volume.Name, 561 } 562 563 if volume.Host.SourcePath != nil { 564 l["host_path"] = *volume.Host.SourcePath 565 } 566 567 result = append(result, l) 568 } 569 return result 570 } 571 572 // Flattens an array of ECS LoadBalancers into a []map[string]interface{} 573 func flattenEcsLoadBalancers(list []*ecs.LoadBalancer) []map[string]interface{} { 574 result := make([]map[string]interface{}, 0, len(list)) 575 for _, loadBalancer := range list { 576 l := map[string]interface{}{ 577 "container_name": *loadBalancer.ContainerName, 578 "container_port": *loadBalancer.ContainerPort, 579 } 580 581 if loadBalancer.LoadBalancerName != nil { 582 l["elb_name"] = *loadBalancer.LoadBalancerName 583 } 584 585 if loadBalancer.TargetGroupArn != nil { 586 l["target_group_arn"] = *loadBalancer.TargetGroupArn 587 } 588 589 result = append(result, l) 590 } 591 return result 592 } 593 594 // Encodes an array of ecs.ContainerDefinitions into a JSON string 595 func flattenEcsContainerDefinitions(definitions []*ecs.ContainerDefinition) (string, error) { 596 byteArray, err := json.Marshal(definitions) 597 if err != nil { 598 return "", fmt.Errorf("Error encoding to JSON: %s", err) 599 } 600 601 n := bytes.Index(byteArray, []byte{0}) 602 return string(byteArray[:n]), nil 603 } 604 605 // Flattens an array of Options into a []map[string]interface{} 606 func flattenOptions(list []*rds.Option) []map[string]interface{} { 607 result := make([]map[string]interface{}, 0, len(list)) 608 for _, i := range list { 609 if i.OptionName != nil { 610 r := make(map[string]interface{}) 611 r["option_name"] = strings.ToLower(*i.OptionName) 612 // Default empty string, guard against nil parameter values 613 r["port"] = "" 614 if i.Port != nil { 615 r["port"] = int(*i.Port) 616 } 617 if i.VpcSecurityGroupMemberships != nil { 618 vpcs := make([]string, 0, len(i.VpcSecurityGroupMemberships)) 619 for _, vpc := range i.VpcSecurityGroupMemberships { 620 id := vpc.VpcSecurityGroupId 621 vpcs = append(vpcs, *id) 622 } 623 624 r["vpc_security_group_memberships"] = vpcs 625 } 626 if i.DBSecurityGroupMemberships != nil { 627 dbs := make([]string, 0, len(i.DBSecurityGroupMemberships)) 628 for _, db := range i.DBSecurityGroupMemberships { 629 id := db.DBSecurityGroupName 630 dbs = append(dbs, *id) 631 } 632 633 r["db_security_group_memberships"] = dbs 634 } 635 if i.OptionSettings != nil { 636 settings := make([]map[string]interface{}, 0, len(i.OptionSettings)) 637 for _, j := range i.OptionSettings { 638 setting := map[string]interface{}{ 639 "name": *j.Name, 640 } 641 if j.Value != nil { 642 setting["value"] = *j.Value 643 } 644 645 settings = append(settings, setting) 646 } 647 648 r["option_settings"] = settings 649 } 650 result = append(result, r) 651 } 652 } 653 return result 654 } 655 656 // Flattens an array of Parameters into a []map[string]interface{} 657 func flattenParameters(list []*rds.Parameter) []map[string]interface{} { 658 result := make([]map[string]interface{}, 0, len(list)) 659 for _, i := range list { 660 if i.ParameterName != nil { 661 r := make(map[string]interface{}) 662 r["name"] = strings.ToLower(*i.ParameterName) 663 // Default empty string, guard against nil parameter values 664 r["value"] = "" 665 if i.ParameterValue != nil { 666 r["value"] = strings.ToLower(*i.ParameterValue) 667 } 668 if i.ApplyMethod != nil { 669 r["apply_method"] = strings.ToLower(*i.ApplyMethod) 670 } 671 672 result = append(result, r) 673 } 674 } 675 return result 676 } 677 678 // Flattens an array of Redshift Parameters into a []map[string]interface{} 679 func flattenRedshiftParameters(list []*redshift.Parameter) []map[string]interface{} { 680 result := make([]map[string]interface{}, 0, len(list)) 681 for _, i := range list { 682 result = append(result, map[string]interface{}{ 683 "name": strings.ToLower(*i.ParameterName), 684 "value": strings.ToLower(*i.ParameterValue), 685 }) 686 } 687 return result 688 } 689 690 // Flattens an array of Parameters into a []map[string]interface{} 691 func flattenElastiCacheParameters(list []*elasticache.Parameter) []map[string]interface{} { 692 result := make([]map[string]interface{}, 0, len(list)) 693 for _, i := range list { 694 if i.ParameterValue != nil { 695 result = append(result, map[string]interface{}{ 696 "name": strings.ToLower(*i.ParameterName), 697 "value": *i.ParameterValue, 698 }) 699 } 700 } 701 return result 702 } 703 704 // Takes the result of flatmap.Expand for an array of strings 705 // and returns a []*string 706 func expandStringList(configured []interface{}) []*string { 707 vs := make([]*string, 0, len(configured)) 708 for _, v := range configured { 709 val, ok := v.(string) 710 if ok && val != "" { 711 vs = append(vs, aws.String(v.(string))) 712 } 713 } 714 return vs 715 } 716 717 // Takes the result of schema.Set of strings and returns a []*string 718 func expandStringSet(configured *schema.Set) []*string { 719 return expandStringList(configured.List()) 720 } 721 722 // Takes list of pointers to strings. Expand to an array 723 // of raw strings and returns a []interface{} 724 // to keep compatibility w/ schema.NewSetschema.NewSet 725 func flattenStringList(list []*string) []interface{} { 726 vs := make([]interface{}, 0, len(list)) 727 for _, v := range list { 728 vs = append(vs, *v) 729 } 730 return vs 731 } 732 733 //Flattens an array of private ip addresses into a []string, where the elements returned are the IP strings e.g. "192.168.0.0" 734 func flattenNetworkInterfacesPrivateIPAddresses(dtos []*ec2.NetworkInterfacePrivateIpAddress) []string { 735 ips := make([]string, 0, len(dtos)) 736 for _, v := range dtos { 737 ip := *v.PrivateIpAddress 738 ips = append(ips, ip) 739 } 740 return ips 741 } 742 743 //Flattens security group identifiers into a []string, where the elements returned are the GroupIDs 744 func flattenGroupIdentifiers(dtos []*ec2.GroupIdentifier) []string { 745 ids := make([]string, 0, len(dtos)) 746 for _, v := range dtos { 747 group_id := *v.GroupId 748 ids = append(ids, group_id) 749 } 750 return ids 751 } 752 753 //Expands an array of IPs into a ec2 Private IP Address Spec 754 func expandPrivateIPAddresses(ips []interface{}) []*ec2.PrivateIpAddressSpecification { 755 dtos := make([]*ec2.PrivateIpAddressSpecification, 0, len(ips)) 756 for i, v := range ips { 757 new_private_ip := &ec2.PrivateIpAddressSpecification{ 758 PrivateIpAddress: aws.String(v.(string)), 759 } 760 761 new_private_ip.Primary = aws.Bool(i == 0) 762 763 dtos = append(dtos, new_private_ip) 764 } 765 return dtos 766 } 767 768 //Flattens network interface attachment into a map[string]interface 769 func flattenAttachment(a *ec2.NetworkInterfaceAttachment) map[string]interface{} { 770 att := make(map[string]interface{}) 771 if a.InstanceId != nil { 772 att["instance"] = *a.InstanceId 773 } 774 att["device_index"] = *a.DeviceIndex 775 att["attachment_id"] = *a.AttachmentId 776 return att 777 } 778 779 func flattenElastiCacheSecurityGroupNames(securityGroups []*elasticache.CacheSecurityGroupMembership) []string { 780 result := make([]string, 0, len(securityGroups)) 781 for _, sg := range securityGroups { 782 if sg.CacheSecurityGroupName != nil { 783 result = append(result, *sg.CacheSecurityGroupName) 784 } 785 } 786 return result 787 } 788 789 func flattenElastiCacheSecurityGroupIds(securityGroups []*elasticache.SecurityGroupMembership) []string { 790 result := make([]string, 0, len(securityGroups)) 791 for _, sg := range securityGroups { 792 if sg.SecurityGroupId != nil { 793 result = append(result, *sg.SecurityGroupId) 794 } 795 } 796 return result 797 } 798 799 // Flattens step adjustments into a list of map[string]interface. 800 func flattenStepAdjustments(adjustments []*autoscaling.StepAdjustment) []map[string]interface{} { 801 result := make([]map[string]interface{}, 0, len(adjustments)) 802 for _, raw := range adjustments { 803 a := map[string]interface{}{ 804 "scaling_adjustment": *raw.ScalingAdjustment, 805 } 806 if raw.MetricIntervalUpperBound != nil { 807 a["metric_interval_upper_bound"] = *raw.MetricIntervalUpperBound 808 } 809 if raw.MetricIntervalLowerBound != nil { 810 a["metric_interval_lower_bound"] = *raw.MetricIntervalLowerBound 811 } 812 result = append(result, a) 813 } 814 return result 815 } 816 817 func flattenResourceRecords(recs []*route53.ResourceRecord, typeStr string) []string { 818 strs := make([]string, 0, len(recs)) 819 for _, r := range recs { 820 if r.Value != nil { 821 s := *r.Value 822 if typeStr == "TXT" || typeStr == "SPF" { 823 s = expandTxtEntry(s) 824 } 825 strs = append(strs, s) 826 } 827 } 828 return strs 829 } 830 831 func expandResourceRecords(recs []interface{}, typeStr string) []*route53.ResourceRecord { 832 records := make([]*route53.ResourceRecord, 0, len(recs)) 833 for _, r := range recs { 834 s := r.(string) 835 if typeStr == "TXT" || typeStr == "SPF" { 836 s = flattenTxtEntry(s) 837 } 838 records = append(records, &route53.ResourceRecord{Value: aws.String(s)}) 839 } 840 return records 841 } 842 843 // How 'flattenTxtEntry' and 'expandTxtEntry' work. 844 // 845 // In the Route 53, TXT entries are written using quoted strings, one per line. 846 // Example: 847 // "x=foo" 848 // "bar=12" 849 // 850 // In Terraform, there are two differences: 851 // - We use a list of strings instead of separating strings with newlines. 852 // - Within each string, we dont' include the surrounding quotes. 853 // Example: 854 // records = ["x=foo", "bar=12"] # Instead of ["\"x=foo\", \"bar=12\""] 855 // 856 // When we pull from Route 53, `expandTxtEntry` removes the surrounding quotes; 857 // when we push to Route 53, `flattenTxtEntry` adds them back. 858 // 859 // One complication is that a single TXT entry can have multiple quoted strings. 860 // For example, here are two TXT entries, one with two quoted strings and the 861 // other with three. 862 // "x=" "foo" 863 // "ba" "r" "=12" 864 // 865 // DNS clients are expected to merge the quoted strings before interpreting the 866 // value. Since `expandTxtEntry` only removes the quotes at the end we can still 867 // (hackily) represent the above configuration in Terraform: 868 // records = ["x=\" \"foo", "ba\" \"r\" \"=12"] 869 // 870 // The primary reason to use multiple strings for an entry is that DNS (and Route 871 // 53) doesn't allow a quoted string to be more than 255 characters long. If you 872 // want a longer TXT entry, you must use multiple quoted strings. 873 // 874 // It would be nice if this Terraform automatically split strings longer than 255 875 // characters. For example, imagine "xxx..xxx" has 256 "x" characters. 876 // records = ["xxx..xxx"] 877 // When pushing to Route 53, this could be converted to: 878 // "xxx..xx" "x" 879 // 880 // This could also work when the user is already using multiple quoted strings: 881 // records = ["xxx.xxx\" \"yyy..yyy"] 882 // When pushing to Route 53, this could be converted to: 883 // "xxx..xx" "xyyy...y" "yy" 884 // 885 // If you want to add this feature, make sure to follow all the quoting rules in 886 // <https://tools.ietf.org/html/rfc1464#section-2>. If you make a mistake, people 887 // might end up relying on that mistake so fixing it would be a breaking change. 888 889 func flattenTxtEntry(s string) string { 890 return fmt.Sprintf(`"%s"`, s) 891 } 892 893 func expandTxtEntry(s string) string { 894 last := len(s) - 1 895 if last != 0 && s[0] == '"' && s[last] == '"' { 896 s = s[1:last] 897 } 898 return s 899 } 900 901 func expandESClusterConfig(m map[string]interface{}) *elasticsearch.ElasticsearchClusterConfig { 902 config := elasticsearch.ElasticsearchClusterConfig{} 903 904 if v, ok := m["dedicated_master_enabled"]; ok { 905 isEnabled := v.(bool) 906 config.DedicatedMasterEnabled = aws.Bool(isEnabled) 907 908 if isEnabled { 909 if v, ok := m["dedicated_master_count"]; ok && v.(int) > 0 { 910 config.DedicatedMasterCount = aws.Int64(int64(v.(int))) 911 } 912 if v, ok := m["dedicated_master_type"]; ok && v.(string) != "" { 913 config.DedicatedMasterType = aws.String(v.(string)) 914 } 915 } 916 } 917 918 if v, ok := m["instance_count"]; ok { 919 config.InstanceCount = aws.Int64(int64(v.(int))) 920 } 921 if v, ok := m["instance_type"]; ok { 922 config.InstanceType = aws.String(v.(string)) 923 } 924 925 if v, ok := m["zone_awareness_enabled"]; ok { 926 config.ZoneAwarenessEnabled = aws.Bool(v.(bool)) 927 } 928 929 return &config 930 } 931 932 func flattenESClusterConfig(c *elasticsearch.ElasticsearchClusterConfig) []map[string]interface{} { 933 m := map[string]interface{}{} 934 935 if c.DedicatedMasterCount != nil { 936 m["dedicated_master_count"] = *c.DedicatedMasterCount 937 } 938 if c.DedicatedMasterEnabled != nil { 939 m["dedicated_master_enabled"] = *c.DedicatedMasterEnabled 940 } 941 if c.DedicatedMasterType != nil { 942 m["dedicated_master_type"] = *c.DedicatedMasterType 943 } 944 if c.InstanceCount != nil { 945 m["instance_count"] = *c.InstanceCount 946 } 947 if c.InstanceType != nil { 948 m["instance_type"] = *c.InstanceType 949 } 950 if c.ZoneAwarenessEnabled != nil { 951 m["zone_awareness_enabled"] = *c.ZoneAwarenessEnabled 952 } 953 954 return []map[string]interface{}{m} 955 } 956 957 func flattenESEBSOptions(o *elasticsearch.EBSOptions) []map[string]interface{} { 958 m := map[string]interface{}{} 959 960 if o.EBSEnabled != nil { 961 m["ebs_enabled"] = *o.EBSEnabled 962 } 963 if o.Iops != nil { 964 m["iops"] = *o.Iops 965 } 966 if o.VolumeSize != nil { 967 m["volume_size"] = *o.VolumeSize 968 } 969 if o.VolumeType != nil { 970 m["volume_type"] = *o.VolumeType 971 } 972 973 return []map[string]interface{}{m} 974 } 975 976 func expandESEBSOptions(m map[string]interface{}) *elasticsearch.EBSOptions { 977 options := elasticsearch.EBSOptions{} 978 979 if v, ok := m["ebs_enabled"]; ok { 980 options.EBSEnabled = aws.Bool(v.(bool)) 981 } 982 if v, ok := m["iops"]; ok && v.(int) > 0 { 983 options.Iops = aws.Int64(int64(v.(int))) 984 } 985 if v, ok := m["volume_size"]; ok && v.(int) > 0 { 986 options.VolumeSize = aws.Int64(int64(v.(int))) 987 } 988 if v, ok := m["volume_type"]; ok && v.(string) != "" { 989 options.VolumeType = aws.String(v.(string)) 990 } 991 992 return &options 993 } 994 995 func expandConfigRecordingGroup(configured []interface{}) *configservice.RecordingGroup { 996 recordingGroup := configservice.RecordingGroup{} 997 group := configured[0].(map[string]interface{}) 998 999 if v, ok := group["all_supported"]; ok { 1000 recordingGroup.AllSupported = aws.Bool(v.(bool)) 1001 } 1002 1003 if v, ok := group["include_global_resource_types"]; ok { 1004 recordingGroup.IncludeGlobalResourceTypes = aws.Bool(v.(bool)) 1005 } 1006 1007 if v, ok := group["resource_types"]; ok { 1008 recordingGroup.ResourceTypes = expandStringList(v.(*schema.Set).List()) 1009 } 1010 return &recordingGroup 1011 } 1012 1013 func flattenConfigRecordingGroup(g *configservice.RecordingGroup) []map[string]interface{} { 1014 m := make(map[string]interface{}, 1) 1015 1016 if g.AllSupported != nil { 1017 m["all_supported"] = *g.AllSupported 1018 } 1019 1020 if g.IncludeGlobalResourceTypes != nil { 1021 m["include_global_resource_types"] = *g.IncludeGlobalResourceTypes 1022 } 1023 1024 if g.ResourceTypes != nil && len(g.ResourceTypes) > 0 { 1025 m["resource_types"] = schema.NewSet(schema.HashString, flattenStringList(g.ResourceTypes)) 1026 } 1027 1028 return []map[string]interface{}{m} 1029 } 1030 1031 func flattenConfigSnapshotDeliveryProperties(p *configservice.ConfigSnapshotDeliveryProperties) []map[string]interface{} { 1032 m := make(map[string]interface{}, 0) 1033 1034 if p.DeliveryFrequency != nil { 1035 m["delivery_frequency"] = *p.DeliveryFrequency 1036 } 1037 1038 return []map[string]interface{}{m} 1039 } 1040 1041 func pointersMapToStringList(pointers map[string]*string) map[string]interface{} { 1042 list := make(map[string]interface{}, len(pointers)) 1043 for i, v := range pointers { 1044 list[i] = *v 1045 } 1046 return list 1047 } 1048 1049 func stringMapToPointers(m map[string]interface{}) map[string]*string { 1050 list := make(map[string]*string, len(m)) 1051 for i, v := range m { 1052 list[i] = aws.String(v.(string)) 1053 } 1054 return list 1055 } 1056 1057 func flattenDSVpcSettings( 1058 s *directoryservice.DirectoryVpcSettingsDescription) []map[string]interface{} { 1059 settings := make(map[string]interface{}, 0) 1060 1061 if s == nil { 1062 return nil 1063 } 1064 1065 settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) 1066 settings["vpc_id"] = *s.VpcId 1067 1068 return []map[string]interface{}{settings} 1069 } 1070 1071 func flattenLambdaEnvironment(lambdaEnv *lambda.EnvironmentResponse) []interface{} { 1072 envs := make(map[string]interface{}) 1073 en := make(map[string]string) 1074 1075 if lambdaEnv == nil { 1076 return nil 1077 } 1078 1079 for k, v := range lambdaEnv.Variables { 1080 en[k] = *v 1081 } 1082 if len(en) > 0 { 1083 envs["variables"] = en 1084 } 1085 1086 return []interface{}{envs} 1087 } 1088 1089 func flattenLambdaVpcConfigResponse(s *lambda.VpcConfigResponse) []map[string]interface{} { 1090 settings := make(map[string]interface{}, 0) 1091 1092 if s == nil { 1093 return nil 1094 } 1095 1096 var emptyVpc bool 1097 if s.VpcId == nil || *s.VpcId == "" { 1098 emptyVpc = true 1099 } 1100 if len(s.SubnetIds) == 0 && len(s.SecurityGroupIds) == 0 && emptyVpc { 1101 return nil 1102 } 1103 1104 settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) 1105 settings["security_group_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SecurityGroupIds)) 1106 if s.VpcId != nil { 1107 settings["vpc_id"] = *s.VpcId 1108 } 1109 1110 return []map[string]interface{}{settings} 1111 } 1112 1113 func flattenDSConnectSettings( 1114 customerDnsIps []*string, 1115 s *directoryservice.DirectoryConnectSettingsDescription) []map[string]interface{} { 1116 if s == nil { 1117 return nil 1118 } 1119 1120 settings := make(map[string]interface{}, 0) 1121 1122 settings["customer_dns_ips"] = schema.NewSet(schema.HashString, flattenStringList(customerDnsIps)) 1123 settings["connect_ips"] = schema.NewSet(schema.HashString, flattenStringList(s.ConnectIps)) 1124 settings["customer_username"] = *s.CustomerUserName 1125 settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) 1126 settings["vpc_id"] = *s.VpcId 1127 1128 return []map[string]interface{}{settings} 1129 } 1130 1131 func expandCloudFormationParameters(params map[string]interface{}) []*cloudformation.Parameter { 1132 var cfParams []*cloudformation.Parameter 1133 for k, v := range params { 1134 cfParams = append(cfParams, &cloudformation.Parameter{ 1135 ParameterKey: aws.String(k), 1136 ParameterValue: aws.String(v.(string)), 1137 }) 1138 } 1139 1140 return cfParams 1141 } 1142 1143 // flattenCloudFormationParameters is flattening list of 1144 // *cloudformation.Parameters and only returning existing 1145 // parameters to avoid clash with default values 1146 func flattenCloudFormationParameters(cfParams []*cloudformation.Parameter, 1147 originalParams map[string]interface{}) map[string]interface{} { 1148 params := make(map[string]interface{}, len(cfParams)) 1149 for _, p := range cfParams { 1150 _, isConfigured := originalParams[*p.ParameterKey] 1151 if isConfigured { 1152 params[*p.ParameterKey] = *p.ParameterValue 1153 } 1154 } 1155 return params 1156 } 1157 1158 func flattenAllCloudFormationParameters(cfParams []*cloudformation.Parameter) map[string]interface{} { 1159 params := make(map[string]interface{}, len(cfParams)) 1160 for _, p := range cfParams { 1161 params[*p.ParameterKey] = *p.ParameterValue 1162 } 1163 return params 1164 } 1165 1166 func expandCloudFormationTags(tags map[string]interface{}) []*cloudformation.Tag { 1167 var cfTags []*cloudformation.Tag 1168 for k, v := range tags { 1169 cfTags = append(cfTags, &cloudformation.Tag{ 1170 Key: aws.String(k), 1171 Value: aws.String(v.(string)), 1172 }) 1173 } 1174 return cfTags 1175 } 1176 1177 func flattenCloudFormationTags(cfTags []*cloudformation.Tag) map[string]string { 1178 tags := make(map[string]string, len(cfTags)) 1179 for _, t := range cfTags { 1180 tags[*t.Key] = *t.Value 1181 } 1182 return tags 1183 } 1184 1185 func flattenCloudFormationOutputs(cfOutputs []*cloudformation.Output) map[string]string { 1186 outputs := make(map[string]string, len(cfOutputs)) 1187 for _, o := range cfOutputs { 1188 outputs[*o.OutputKey] = *o.OutputValue 1189 } 1190 return outputs 1191 } 1192 1193 func flattenAsgSuspendedProcesses(list []*autoscaling.SuspendedProcess) []string { 1194 strs := make([]string, 0, len(list)) 1195 for _, r := range list { 1196 if r.ProcessName != nil { 1197 strs = append(strs, *r.ProcessName) 1198 } 1199 } 1200 return strs 1201 } 1202 1203 func flattenAsgEnabledMetrics(list []*autoscaling.EnabledMetric) []string { 1204 strs := make([]string, 0, len(list)) 1205 for _, r := range list { 1206 if r.Metric != nil { 1207 strs = append(strs, *r.Metric) 1208 } 1209 } 1210 return strs 1211 } 1212 1213 func flattenKinesisShardLevelMetrics(list []*kinesis.EnhancedMetrics) []string { 1214 if len(list) == 0 { 1215 return []string{} 1216 } 1217 strs := make([]string, 0, len(list[0].ShardLevelMetrics)) 1218 for _, s := range list[0].ShardLevelMetrics { 1219 strs = append(strs, *s) 1220 } 1221 return strs 1222 } 1223 1224 func flattenApiGatewayStageKeys(keys []*string) []map[string]interface{} { 1225 stageKeys := make([]map[string]interface{}, 0, len(keys)) 1226 for _, o := range keys { 1227 key := make(map[string]interface{}) 1228 parts := strings.Split(*o, "/") 1229 key["stage_name"] = parts[1] 1230 key["rest_api_id"] = parts[0] 1231 1232 stageKeys = append(stageKeys, key) 1233 } 1234 return stageKeys 1235 } 1236 1237 func expandApiGatewayStageKeys(d *schema.ResourceData) []*apigateway.StageKey { 1238 var stageKeys []*apigateway.StageKey 1239 1240 if stageKeyData, ok := d.GetOk("stage_key"); ok { 1241 params := stageKeyData.(*schema.Set).List() 1242 for k := range params { 1243 data := params[k].(map[string]interface{}) 1244 stageKeys = append(stageKeys, &apigateway.StageKey{ 1245 RestApiId: aws.String(data["rest_api_id"].(string)), 1246 StageName: aws.String(data["stage_name"].(string)), 1247 }) 1248 } 1249 } 1250 1251 return stageKeys 1252 } 1253 1254 func expandApiGatewayRequestResponseModelOperations(d *schema.ResourceData, key string, prefix string) []*apigateway.PatchOperation { 1255 operations := make([]*apigateway.PatchOperation, 0) 1256 1257 oldModels, newModels := d.GetChange(key) 1258 oldModelMap := oldModels.(map[string]interface{}) 1259 newModelMap := newModels.(map[string]interface{}) 1260 1261 for k, _ := range oldModelMap { 1262 operation := apigateway.PatchOperation{ 1263 Op: aws.String("remove"), 1264 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))), 1265 } 1266 1267 for nK, nV := range newModelMap { 1268 if nK == k { 1269 operation.Op = aws.String("replace") 1270 operation.Value = aws.String(nV.(string)) 1271 } 1272 } 1273 1274 operations = append(operations, &operation) 1275 } 1276 1277 for nK, nV := range newModelMap { 1278 exists := false 1279 for k, _ := range oldModelMap { 1280 if k == nK { 1281 exists = true 1282 } 1283 } 1284 if !exists { 1285 operation := apigateway.PatchOperation{ 1286 Op: aws.String("add"), 1287 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(nK, "/", "~1", -1))), 1288 Value: aws.String(nV.(string)), 1289 } 1290 operations = append(operations, &operation) 1291 } 1292 } 1293 1294 return operations 1295 } 1296 1297 func deprecatedExpandApiGatewayMethodParametersJSONOperations(d *schema.ResourceData, key string, prefix string) ([]*apigateway.PatchOperation, error) { 1298 operations := make([]*apigateway.PatchOperation, 0) 1299 oldParameters, newParameters := d.GetChange(key) 1300 oldParametersMap := make(map[string]interface{}) 1301 newParametersMap := make(map[string]interface{}) 1302 1303 if err := json.Unmarshal([]byte(oldParameters.(string)), &oldParametersMap); err != nil { 1304 err := fmt.Errorf("Error unmarshaling old %s: %s", key, err) 1305 return operations, err 1306 } 1307 1308 if err := json.Unmarshal([]byte(newParameters.(string)), &newParametersMap); err != nil { 1309 err := fmt.Errorf("Error unmarshaling new %s: %s", key, err) 1310 return operations, err 1311 } 1312 1313 for k, _ := range oldParametersMap { 1314 operation := apigateway.PatchOperation{ 1315 Op: aws.String("remove"), 1316 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, k)), 1317 } 1318 1319 for nK, nV := range newParametersMap { 1320 if nK == k { 1321 operation.Op = aws.String("replace") 1322 operation.Value = aws.String(strconv.FormatBool(nV.(bool))) 1323 } 1324 } 1325 1326 operations = append(operations, &operation) 1327 } 1328 1329 for nK, nV := range newParametersMap { 1330 exists := false 1331 for k, _ := range oldParametersMap { 1332 if k == nK { 1333 exists = true 1334 } 1335 } 1336 if !exists { 1337 operation := apigateway.PatchOperation{ 1338 Op: aws.String("add"), 1339 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, nK)), 1340 Value: aws.String(strconv.FormatBool(nV.(bool))), 1341 } 1342 operations = append(operations, &operation) 1343 } 1344 } 1345 1346 return operations, nil 1347 } 1348 1349 func expandApiGatewayMethodParametersOperations(d *schema.ResourceData, key string, prefix string) ([]*apigateway.PatchOperation, error) { 1350 operations := make([]*apigateway.PatchOperation, 0) 1351 1352 oldParameters, newParameters := d.GetChange(key) 1353 oldParametersMap := oldParameters.(map[string]interface{}) 1354 newParametersMap := newParameters.(map[string]interface{}) 1355 1356 for k, _ := range oldParametersMap { 1357 operation := apigateway.PatchOperation{ 1358 Op: aws.String("remove"), 1359 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, k)), 1360 } 1361 1362 for nK, nV := range newParametersMap { 1363 b, ok := nV.(bool) 1364 if !ok { 1365 value, _ := strconv.ParseBool(nV.(string)) 1366 b = value 1367 } 1368 if nK == k { 1369 operation.Op = aws.String("replace") 1370 operation.Value = aws.String(strconv.FormatBool(b)) 1371 } 1372 } 1373 1374 operations = append(operations, &operation) 1375 } 1376 1377 for nK, nV := range newParametersMap { 1378 exists := false 1379 for k, _ := range oldParametersMap { 1380 if k == nK { 1381 exists = true 1382 } 1383 } 1384 if !exists { 1385 b, ok := nV.(bool) 1386 if !ok { 1387 value, _ := strconv.ParseBool(nV.(string)) 1388 b = value 1389 } 1390 operation := apigateway.PatchOperation{ 1391 Op: aws.String("add"), 1392 Path: aws.String(fmt.Sprintf("/%s/%s", prefix, nK)), 1393 Value: aws.String(strconv.FormatBool(b)), 1394 } 1395 operations = append(operations, &operation) 1396 } 1397 } 1398 1399 return operations, nil 1400 } 1401 1402 func expandApiGatewayStageKeyOperations(d *schema.ResourceData) []*apigateway.PatchOperation { 1403 operations := make([]*apigateway.PatchOperation, 0) 1404 1405 prev, curr := d.GetChange("stage_key") 1406 prevList := prev.(*schema.Set).List() 1407 currList := curr.(*schema.Set).List() 1408 1409 for i := range prevList { 1410 p := prevList[i].(map[string]interface{}) 1411 exists := false 1412 1413 for j := range currList { 1414 c := currList[j].(map[string]interface{}) 1415 if c["rest_api_id"].(string) == p["rest_api_id"].(string) && c["stage_name"].(string) == p["stage_name"].(string) { 1416 exists = true 1417 } 1418 } 1419 1420 if !exists { 1421 operations = append(operations, &apigateway.PatchOperation{ 1422 Op: aws.String("remove"), 1423 Path: aws.String("/stages"), 1424 Value: aws.String(fmt.Sprintf("%s/%s", p["rest_api_id"].(string), p["stage_name"].(string))), 1425 }) 1426 } 1427 } 1428 1429 for i := range currList { 1430 c := currList[i].(map[string]interface{}) 1431 exists := false 1432 1433 for j := range prevList { 1434 p := prevList[j].(map[string]interface{}) 1435 if c["rest_api_id"].(string) == p["rest_api_id"].(string) && c["stage_name"].(string) == p["stage_name"].(string) { 1436 exists = true 1437 } 1438 } 1439 1440 if !exists { 1441 operations = append(operations, &apigateway.PatchOperation{ 1442 Op: aws.String("add"), 1443 Path: aws.String("/stages"), 1444 Value: aws.String(fmt.Sprintf("%s/%s", c["rest_api_id"].(string), c["stage_name"].(string))), 1445 }) 1446 } 1447 } 1448 1449 return operations 1450 } 1451 1452 func expandCloudWachLogMetricTransformations(m map[string]interface{}) []*cloudwatchlogs.MetricTransformation { 1453 transformation := cloudwatchlogs.MetricTransformation{ 1454 MetricName: aws.String(m["name"].(string)), 1455 MetricNamespace: aws.String(m["namespace"].(string)), 1456 MetricValue: aws.String(m["value"].(string)), 1457 } 1458 1459 return []*cloudwatchlogs.MetricTransformation{&transformation} 1460 } 1461 1462 func flattenCloudWachLogMetricTransformations(ts []*cloudwatchlogs.MetricTransformation) map[string]string { 1463 m := make(map[string]string, 0) 1464 1465 m["name"] = *ts[0].MetricName 1466 m["namespace"] = *ts[0].MetricNamespace 1467 m["value"] = *ts[0].MetricValue 1468 1469 return m 1470 } 1471 1472 func flattenBeanstalkAsg(list []*elasticbeanstalk.AutoScalingGroup) []string { 1473 strs := make([]string, 0, len(list)) 1474 for _, r := range list { 1475 if r.Name != nil { 1476 strs = append(strs, *r.Name) 1477 } 1478 } 1479 return strs 1480 } 1481 1482 func flattenBeanstalkInstances(list []*elasticbeanstalk.Instance) []string { 1483 strs := make([]string, 0, len(list)) 1484 for _, r := range list { 1485 if r.Id != nil { 1486 strs = append(strs, *r.Id) 1487 } 1488 } 1489 return strs 1490 } 1491 1492 func flattenBeanstalkLc(list []*elasticbeanstalk.LaunchConfiguration) []string { 1493 strs := make([]string, 0, len(list)) 1494 for _, r := range list { 1495 if r.Name != nil { 1496 strs = append(strs, *r.Name) 1497 } 1498 } 1499 return strs 1500 } 1501 1502 func flattenBeanstalkElb(list []*elasticbeanstalk.LoadBalancer) []string { 1503 strs := make([]string, 0, len(list)) 1504 for _, r := range list { 1505 if r.Name != nil { 1506 strs = append(strs, *r.Name) 1507 } 1508 } 1509 return strs 1510 } 1511 1512 func flattenBeanstalkSqs(list []*elasticbeanstalk.Queue) []string { 1513 strs := make([]string, 0, len(list)) 1514 for _, r := range list { 1515 if r.URL != nil { 1516 strs = append(strs, *r.URL) 1517 } 1518 } 1519 return strs 1520 } 1521 1522 func flattenBeanstalkTrigger(list []*elasticbeanstalk.Trigger) []string { 1523 strs := make([]string, 0, len(list)) 1524 for _, r := range list { 1525 if r.Name != nil { 1526 strs = append(strs, *r.Name) 1527 } 1528 } 1529 return strs 1530 } 1531 1532 // There are several parts of the AWS API that will sort lists of strings, 1533 // causing diffs inbetween resources that use lists. This avoids a bit of 1534 // code duplication for pre-sorts that can be used for things like hash 1535 // functions, etc. 1536 func sortInterfaceSlice(in []interface{}) []interface{} { 1537 a := []string{} 1538 b := []interface{}{} 1539 for _, v := range in { 1540 a = append(a, v.(string)) 1541 } 1542 1543 sort.Strings(a) 1544 1545 for _, v := range a { 1546 b = append(b, v) 1547 } 1548 1549 return b 1550 } 1551 1552 // This function sorts List A to look like a list found in the tf file. 1553 func sortListBasedonTFFile(in []string, d *schema.ResourceData, listName string) ([]string, error) { 1554 if attributeCount, ok := d.Get(listName + ".#").(int); ok { 1555 for i := 0; i < attributeCount; i++ { 1556 currAttributeId := d.Get(listName + "." + strconv.Itoa(i)) 1557 for j := 0; j < len(in); j++ { 1558 if currAttributeId == in[j] { 1559 in[i], in[j] = in[j], in[i] 1560 } 1561 } 1562 } 1563 return in, nil 1564 } 1565 return in, fmt.Errorf("Could not find list: %s", listName) 1566 } 1567 1568 func flattenApiGatewayThrottleSettings(settings *apigateway.ThrottleSettings) []map[string]interface{} { 1569 result := make([]map[string]interface{}, 0, 1) 1570 1571 if settings != nil { 1572 r := make(map[string]interface{}) 1573 if settings.BurstLimit != nil { 1574 r["burst_limit"] = *settings.BurstLimit 1575 } 1576 1577 if settings.RateLimit != nil { 1578 r["rate_limit"] = *settings.RateLimit 1579 } 1580 1581 result = append(result, r) 1582 } 1583 1584 return result 1585 } 1586 1587 // TODO: refactor some of these helper functions and types in the terraform/helper packages 1588 1589 // getStringPtr returns a *string version of the value taken from m, where m 1590 // can be a map[string]interface{} or a *schema.ResourceData. If the key isn't 1591 // present or is empty, getNilString returns nil. 1592 func getStringPtr(m interface{}, key string) *string { 1593 switch m := m.(type) { 1594 case map[string]interface{}: 1595 v := m[key] 1596 1597 if v == nil { 1598 return nil 1599 } 1600 1601 s := v.(string) 1602 if s == "" { 1603 return nil 1604 } 1605 1606 return &s 1607 1608 case *schema.ResourceData: 1609 if v, ok := m.GetOk(key); ok { 1610 if v == nil || v.(string) == "" { 1611 return nil 1612 } 1613 s := v.(string) 1614 return &s 1615 } 1616 1617 default: 1618 panic("unknown type in getStringPtr") 1619 } 1620 1621 return nil 1622 } 1623 1624 // getStringPtrList returns a []*string version of the map value. If the key 1625 // isn't present, getNilStringList returns nil. 1626 func getStringPtrList(m map[string]interface{}, key string) []*string { 1627 if v, ok := m[key]; ok { 1628 var stringList []*string 1629 for _, i := range v.([]interface{}) { 1630 s := i.(string) 1631 stringList = append(stringList, &s) 1632 } 1633 1634 return stringList 1635 } 1636 1637 return nil 1638 } 1639 1640 // a convenience wrapper type for the schema.Set map[string]interface{} 1641 // Set operations only alter the underlying map if the value is not nil 1642 type setMap map[string]interface{} 1643 1644 // SetString sets m[key] = *value only if `value != nil` 1645 func (s setMap) SetString(key string, value *string) { 1646 if value == nil { 1647 return 1648 } 1649 1650 s[key] = *value 1651 } 1652 1653 // SetStringMap sets key to value as a map[string]interface{}, stripping any nil 1654 // values. The value parameter can be a map[string]interface{}, a 1655 // map[string]*string, or a map[string]string. 1656 func (s setMap) SetStringMap(key string, value interface{}) { 1657 // because these methods are meant to be chained without intermediate 1658 // checks for nil, we are likely to get interfaces with dynamic types but 1659 // a nil value. 1660 if reflect.ValueOf(value).IsNil() { 1661 return 1662 } 1663 1664 m := make(map[string]interface{}) 1665 1666 switch value := value.(type) { 1667 case map[string]string: 1668 for k, v := range value { 1669 m[k] = v 1670 } 1671 case map[string]*string: 1672 for k, v := range value { 1673 if v == nil { 1674 continue 1675 } 1676 m[k] = *v 1677 } 1678 case map[string]interface{}: 1679 for k, v := range value { 1680 if v == nil { 1681 continue 1682 } 1683 1684 switch v := v.(type) { 1685 case string: 1686 m[k] = v 1687 case *string: 1688 if v != nil { 1689 m[k] = *v 1690 } 1691 default: 1692 panic(fmt.Sprintf("unknown type for SetString: %T", v)) 1693 } 1694 } 1695 } 1696 1697 // catch the case where the interface wasn't nil, but we had no non-nil values 1698 if len(m) > 0 { 1699 s[key] = m 1700 } 1701 } 1702 1703 // Set assigns value to s[key] if value isn't nil 1704 func (s setMap) Set(key string, value interface{}) { 1705 if reflect.ValueOf(value).IsNil() { 1706 return 1707 } 1708 1709 s[key] = value 1710 } 1711 1712 // Map returns the raw map type for a shorter type conversion 1713 func (s setMap) Map() map[string]interface{} { 1714 return map[string]interface{}(s) 1715 } 1716 1717 // MapList returns the map[string]interface{} as a single element in a slice to 1718 // match the schema.Set data type used for structs. 1719 func (s setMap) MapList() []map[string]interface{} { 1720 return []map[string]interface{}{s.Map()} 1721 } 1722 1723 // Takes the result of flatmap.Expand for an array of policy attributes and 1724 // returns ELB API compatible objects 1725 func expandPolicyAttributes(configured []interface{}) ([]*elb.PolicyAttribute, error) { 1726 attributes := make([]*elb.PolicyAttribute, 0, len(configured)) 1727 1728 // Loop over our configured attributes and create 1729 // an array of aws-sdk-go compatible objects 1730 for _, lRaw := range configured { 1731 data := lRaw.(map[string]interface{}) 1732 1733 a := &elb.PolicyAttribute{ 1734 AttributeName: aws.String(data["name"].(string)), 1735 AttributeValue: aws.String(data["value"].(string)), 1736 } 1737 1738 attributes = append(attributes, a) 1739 1740 } 1741 1742 return attributes, nil 1743 } 1744 1745 // Flattens an array of PolicyAttributes into a []interface{} 1746 func flattenPolicyAttributes(list []*elb.PolicyAttributeDescription) []interface{} { 1747 attributes := []interface{}{} 1748 for _, attrdef := range list { 1749 attribute := map[string]string{ 1750 "name": *attrdef.AttributeName, 1751 "value": *attrdef.AttributeValue, 1752 } 1753 1754 attributes = append(attributes, attribute) 1755 1756 } 1757 1758 return attributes 1759 } 1760 1761 func flattenConfigRuleSource(source *configservice.Source) []interface{} { 1762 var result []interface{} 1763 m := make(map[string]interface{}) 1764 m["owner"] = *source.Owner 1765 m["source_identifier"] = *source.SourceIdentifier 1766 if len(source.SourceDetails) > 0 { 1767 m["source_detail"] = schema.NewSet(configRuleSourceDetailsHash, flattenConfigRuleSourceDetails(source.SourceDetails)) 1768 } 1769 result = append(result, m) 1770 return result 1771 } 1772 1773 func flattenConfigRuleSourceDetails(details []*configservice.SourceDetail) []interface{} { 1774 var items []interface{} 1775 for _, d := range details { 1776 m := make(map[string]interface{}) 1777 if d.MessageType != nil { 1778 m["message_type"] = *d.MessageType 1779 } 1780 if d.EventSource != nil { 1781 m["event_source"] = *d.EventSource 1782 } 1783 if d.MaximumExecutionFrequency != nil { 1784 m["maximum_execution_frequency"] = *d.MaximumExecutionFrequency 1785 } 1786 1787 items = append(items, m) 1788 } 1789 1790 return items 1791 } 1792 1793 func expandConfigRuleSource(configured []interface{}) *configservice.Source { 1794 cfg := configured[0].(map[string]interface{}) 1795 source := configservice.Source{ 1796 Owner: aws.String(cfg["owner"].(string)), 1797 SourceIdentifier: aws.String(cfg["source_identifier"].(string)), 1798 } 1799 if details, ok := cfg["source_detail"]; ok { 1800 source.SourceDetails = expandConfigRuleSourceDetails(details.(*schema.Set)) 1801 } 1802 return &source 1803 } 1804 1805 func expandConfigRuleSourceDetails(configured *schema.Set) []*configservice.SourceDetail { 1806 var results []*configservice.SourceDetail 1807 1808 for _, item := range configured.List() { 1809 detail := item.(map[string]interface{}) 1810 src := configservice.SourceDetail{} 1811 1812 if msgType, ok := detail["message_type"].(string); ok && msgType != "" { 1813 src.MessageType = aws.String(msgType) 1814 } 1815 if eventSource, ok := detail["event_source"].(string); ok && eventSource != "" { 1816 src.EventSource = aws.String(eventSource) 1817 } 1818 if maxExecFreq, ok := detail["maximum_execution_frequency"].(string); ok && maxExecFreq != "" { 1819 src.MaximumExecutionFrequency = aws.String(maxExecFreq) 1820 } 1821 1822 results = append(results, &src) 1823 } 1824 1825 return results 1826 } 1827 1828 func flattenConfigRuleScope(scope *configservice.Scope) []interface{} { 1829 var items []interface{} 1830 1831 m := make(map[string]interface{}) 1832 if scope.ComplianceResourceId != nil { 1833 m["compliance_resource_id"] = *scope.ComplianceResourceId 1834 } 1835 if scope.ComplianceResourceTypes != nil { 1836 m["compliance_resource_types"] = schema.NewSet(schema.HashString, flattenStringList(scope.ComplianceResourceTypes)) 1837 } 1838 if scope.TagKey != nil { 1839 m["tag_key"] = *scope.TagKey 1840 } 1841 if scope.TagValue != nil { 1842 m["tag_value"] = *scope.TagValue 1843 } 1844 1845 items = append(items, m) 1846 return items 1847 } 1848 1849 func expandConfigRuleScope(configured map[string]interface{}) *configservice.Scope { 1850 scope := &configservice.Scope{} 1851 1852 if v, ok := configured["compliance_resource_id"].(string); ok && v != "" { 1853 scope.ComplianceResourceId = aws.String(v) 1854 } 1855 if v, ok := configured["compliance_resource_types"]; ok { 1856 l := v.(*schema.Set) 1857 if l.Len() > 0 { 1858 scope.ComplianceResourceTypes = expandStringList(l.List()) 1859 } 1860 } 1861 if v, ok := configured["tag_key"].(string); ok && v != "" { 1862 scope.TagKey = aws.String(v) 1863 } 1864 if v, ok := configured["tag_value"].(string); ok && v != "" { 1865 scope.TagValue = aws.String(v) 1866 } 1867 1868 return scope 1869 } 1870 1871 // Takes a value containing JSON string and passes it through 1872 // the JSON parser to normalize it, returns either a parsing 1873 // error or normalized JSON string. 1874 func normalizeJsonString(jsonString interface{}) (string, error) { 1875 var j interface{} 1876 1877 if jsonString == nil || jsonString.(string) == "" { 1878 return "", nil 1879 } 1880 1881 s := jsonString.(string) 1882 1883 err := json.Unmarshal([]byte(s), &j) 1884 if err != nil { 1885 return s, err 1886 } 1887 1888 bytes, _ := json.Marshal(j) 1889 return string(bytes[:]), nil 1890 } 1891 1892 // Takes a value containing YAML string and passes it through 1893 // the YAML parser. Returns either a parsing 1894 // error or original YAML string. 1895 func checkYamlString(yamlString interface{}) (string, error) { 1896 var y interface{} 1897 1898 if yamlString == nil || yamlString.(string) == "" { 1899 return "", nil 1900 } 1901 1902 s := yamlString.(string) 1903 1904 err := yaml.Unmarshal([]byte(s), &y) 1905 if err != nil { 1906 return s, err 1907 } 1908 1909 return s, nil 1910 } 1911 1912 func normalizeCloudFormationTemplate(templateString interface{}) (string, error) { 1913 if looksLikeJsonString(templateString) { 1914 return normalizeJsonString(templateString) 1915 } else { 1916 return checkYamlString(templateString) 1917 } 1918 } 1919 1920 func flattenInspectorTags(cfTags []*cloudformation.Tag) map[string]string { 1921 tags := make(map[string]string, len(cfTags)) 1922 for _, t := range cfTags { 1923 tags[*t.Key] = *t.Value 1924 } 1925 return tags 1926 } 1927 1928 func flattenApiGatewayUsageApiStages(s []*apigateway.ApiStage) []map[string]interface{} { 1929 stages := make([]map[string]interface{}, 0) 1930 1931 for _, bd := range s { 1932 if bd.ApiId != nil && bd.Stage != nil { 1933 stage := make(map[string]interface{}) 1934 stage["api_id"] = *bd.ApiId 1935 stage["stage"] = *bd.Stage 1936 1937 stages = append(stages, stage) 1938 } 1939 } 1940 1941 if len(stages) > 0 { 1942 return stages 1943 } 1944 1945 return nil 1946 } 1947 1948 func flattenApiGatewayUsagePlanThrottling(s *apigateway.ThrottleSettings) []map[string]interface{} { 1949 settings := make(map[string]interface{}, 0) 1950 1951 if s == nil { 1952 return nil 1953 } 1954 1955 if s.BurstLimit != nil { 1956 settings["burst_limit"] = *s.BurstLimit 1957 } 1958 1959 if s.RateLimit != nil { 1960 settings["rate_limit"] = *s.RateLimit 1961 } 1962 1963 return []map[string]interface{}{settings} 1964 } 1965 1966 func flattenApiGatewayUsagePlanQuota(s *apigateway.QuotaSettings) []map[string]interface{} { 1967 settings := make(map[string]interface{}, 0) 1968 1969 if s == nil { 1970 return nil 1971 } 1972 1973 if s.Limit != nil { 1974 settings["limit"] = *s.Limit 1975 } 1976 1977 if s.Offset != nil { 1978 settings["offset"] = *s.Offset 1979 } 1980 1981 if s.Period != nil { 1982 settings["period"] = *s.Period 1983 } 1984 1985 return []map[string]interface{}{settings} 1986 } 1987 1988 func buildApiGatewayInvokeURL(restApiId, region, stageName string) string { 1989 return fmt.Sprintf("https://%s.execute-api.%s.amazonaws.com/%s", 1990 restApiId, region, stageName) 1991 } 1992 1993 func buildApiGatewayExecutionARN(restApiId, region, accountId string) (string, error) { 1994 if accountId == "" { 1995 return "", fmt.Errorf("Unable to build execution ARN for %s as account ID is missing", 1996 restApiId) 1997 } 1998 return fmt.Sprintf("arn:aws:execute-api:%s:%s:%s", 1999 region, accountId, restApiId), nil 2000 } 2001 2002 func expandCognitoSupportedLoginProviders(config map[string]interface{}) map[string]*string { 2003 m := map[string]*string{} 2004 for k, v := range config { 2005 s := v.(string) 2006 m[k] = &s 2007 } 2008 return m 2009 } 2010 2011 func flattenCognitoSupportedLoginProviders(config map[string]*string) map[string]string { 2012 m := map[string]string{} 2013 for k, v := range config { 2014 m[k] = *v 2015 } 2016 return m 2017 } 2018 2019 func expandCognitoIdentityProviders(s *schema.Set) []*cognitoidentity.Provider { 2020 ips := make([]*cognitoidentity.Provider, 0) 2021 2022 for _, v := range s.List() { 2023 s := v.(map[string]interface{}) 2024 2025 ip := &cognitoidentity.Provider{} 2026 2027 if sv, ok := s["client_id"].(string); ok { 2028 ip.ClientId = aws.String(sv) 2029 } 2030 2031 if sv, ok := s["provider_name"].(string); ok { 2032 ip.ProviderName = aws.String(sv) 2033 } 2034 2035 if sv, ok := s["server_side_token_check"].(bool); ok { 2036 ip.ServerSideTokenCheck = aws.Bool(sv) 2037 } 2038 2039 ips = append(ips, ip) 2040 } 2041 2042 return ips 2043 } 2044 2045 func flattenCognitoIdentityProviders(ips []*cognitoidentity.Provider) []map[string]interface{} { 2046 values := make([]map[string]interface{}, 0) 2047 2048 for _, v := range ips { 2049 ip := make(map[string]interface{}) 2050 2051 if v == nil { 2052 return nil 2053 } 2054 2055 if v.ClientId != nil { 2056 ip["client_id"] = *v.ClientId 2057 } 2058 2059 if v.ProviderName != nil { 2060 ip["provider_name"] = *v.ProviderName 2061 } 2062 2063 if v.ServerSideTokenCheck != nil { 2064 ip["server_side_token_check"] = *v.ServerSideTokenCheck 2065 } 2066 2067 values = append(values, ip) 2068 } 2069 2070 return values 2071 } 2072 2073 func buildLambdaInvokeArn(lambdaArn, region string) string { 2074 apiVersion := "2015-03-31" 2075 return fmt.Sprintf("arn:aws:apigateway:%s:lambda:path/%s/functions/%s/invocations", 2076 region, apiVersion, lambdaArn) 2077 } 2078 2079 func sliceContainsMap(l []interface{}, m map[string]interface{}) (int, bool) { 2080 for i, t := range l { 2081 if reflect.DeepEqual(m, t.(map[string]interface{})) { 2082 return i, true 2083 } 2084 } 2085 2086 return -1, false 2087 }