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