terraform/builtin/providers/aws/structure.go

2128 lines
57 KiB
Go
Raw Normal View History

package aws
import (
"bytes"
"encoding/json"
2015-04-16 22:18:01 +02:00
"fmt"
"reflect"
"sort"
"strconv"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/cognitoidentity"
"github.com/aws/aws-sdk-go/service/configservice"
"github.com/aws/aws-sdk-go/service/directoryservice"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/aws/aws-sdk-go/service/elasticache"
"github.com/aws/aws-sdk-go/service/elasticbeanstalk"
elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/aws/aws-sdk-go/service/kinesis"
"github.com/aws/aws-sdk-go/service/lambda"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/redshift"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/hashicorp/terraform/helper/schema"
"gopkg.in/yaml.v2"
)
// Takes the result of flatmap.Expand for an array of listeners and
// returns ELB API compatible objects
2015-04-16 22:28:18 +02:00
func expandListeners(configured []interface{}) ([]*elb.Listener, error) {
2015-04-16 22:18:01 +02:00
listeners := make([]*elb.Listener, 0, len(configured))
// Loop over our configured listeners and create
2016-09-12 08:14:24 +02:00
// an array of aws-sdk-go compatible objects
for _, lRaw := range configured {
data := lRaw.(map[string]interface{})
2014-07-25 00:50:18 +02:00
2015-04-16 22:18:01 +02:00
ip := int64(data["instance_port"].(int))
lp := int64(data["lb_port"].(int))
l := &elb.Listener{
InstancePort: &ip,
InstanceProtocol: aws.String(data["instance_protocol"].(string)),
2015-04-16 22:18:01 +02:00
LoadBalancerPort: &lp,
Protocol: aws.String(data["lb_protocol"].(string)),
}
if v, ok := data["ssl_certificate_id"]; ok {
l.SSLCertificateId = aws.String(v.(string))
}
2015-11-13 20:53:52 +01:00
var valid bool
if l.SSLCertificateId != nil && *l.SSLCertificateId != "" {
// validate the protocol is correct
for _, p := range []string{"https", "ssl"} {
if (strings.ToLower(*l.InstanceProtocol) == p) || (strings.ToLower(*l.Protocol) == p) {
2015-11-13 20:53:52 +01:00
valid = true
}
}
} else {
valid = true
}
2015-11-13 20:53:52 +01:00
if valid {
listeners = append(listeners, l)
} else {
return nil, fmt.Errorf("[ERR] ELB Listener: ssl_certificate_id may be set only when protocol is 'https' or 'ssl'")
}
}
2014-07-25 00:50:18 +02:00
return listeners, nil
}
// Takes the result of flatmap. Expand for an array of listeners and
// returns ECS Volume compatible objects
func expandEcsVolumes(configured []interface{}) ([]*ecs.Volume, error) {
volumes := make([]*ecs.Volume, 0, len(configured))
// Loop over our configured volumes and create
// an array of aws-sdk-go compatible objects
for _, lRaw := range configured {
data := lRaw.(map[string]interface{})
l := &ecs.Volume{
Name: aws.String(data["name"].(string)),
}
hostPath := data["host_path"].(string)
if hostPath != "" {
l.Host = &ecs.HostVolumeProperties{
SourcePath: aws.String(hostPath),
}
}
volumes = append(volumes, l)
}
return volumes, nil
}
// Takes JSON in a string. Decodes JSON into
// an array of ecs.ContainerDefinition compatible objects
func expandEcsContainerDefinitions(rawDefinitions string) ([]*ecs.ContainerDefinition, error) {
var definitions []*ecs.ContainerDefinition
err := json.Unmarshal([]byte(rawDefinitions), &definitions)
if err != nil {
return nil, fmt.Errorf("Error decoding JSON: %s", err)
}
return definitions, nil
}
// Takes the result of flatmap. Expand for an array of load balancers and
// returns ecs.LoadBalancer compatible objects
func expandEcsLoadBalancers(configured []interface{}) []*ecs.LoadBalancer {
loadBalancers := make([]*ecs.LoadBalancer, 0, len(configured))
// Loop over our configured load balancers and create
// an array of aws-sdk-go compatible objects
for _, lRaw := range configured {
data := lRaw.(map[string]interface{})
l := &ecs.LoadBalancer{
ContainerName: aws.String(data["container_name"].(string)),
ContainerPort: aws.Int64(int64(data["container_port"].(int))),
}
if v, ok := data["elb_name"]; ok && v.(string) != "" {
l.LoadBalancerName = aws.String(v.(string))
}
if v, ok := data["target_group_arn"]; ok && v.(string) != "" {
l.TargetGroupArn = aws.String(v.(string))
}
loadBalancers = append(loadBalancers, l)
}
return loadBalancers
}
// Takes the result of flatmap.Expand for an array of ingress/egress security
// group rules and returns EC2 API compatible objects. This function will error
// if it finds invalid permissions input, namely a protocol of "-1" with either
// to_port or from_port set to a non-zero value.
2015-04-16 22:28:18 +02:00
func expandIPPerms(
group *ec2.SecurityGroup, configured []interface{}) ([]*ec2.IpPermission, error) {
vpc := group.VpcId != nil && *group.VpcId != ""
perms := make([]*ec2.IpPermission, len(configured))
for i, mRaw := range configured {
var perm ec2.IpPermission
m := mRaw.(map[string]interface{})
perm.FromPort = aws.Int64(int64(m["from_port"].(int)))
perm.ToPort = aws.Int64(int64(m["to_port"].(int)))
perm.IpProtocol = aws.String(m["protocol"].(string))
// When protocol is "-1", AWS won't store any ports for the
// rule, but also won't error if the user specifies ports other
// than '0'. Force the user to make a deliberate '0' port
// choice when specifying a "-1" protocol, and tell them about
// AWS's behavior in the error message.
if *perm.IpProtocol == "-1" && (*perm.FromPort != 0 || *perm.ToPort != 0) {
return nil, fmt.Errorf(
"from_port (%d) and to_port (%d) must both be 0 to use the 'ALL' \"-1\" protocol!",
*perm.FromPort, *perm.ToPort)
}
var groups []string
if raw, ok := m["security_groups"]; ok {
list := raw.(*schema.Set).List()
for _, v := range list {
groups = append(groups, v.(string))
}
}
if v, ok := m["self"]; ok && v.(bool) {
if vpc {
groups = append(groups, *group.GroupId)
} else {
groups = append(groups, *group.GroupName)
}
}
if len(groups) > 0 {
perm.UserIdGroupPairs = make([]*ec2.UserIdGroupPair, len(groups))
for i, name := range groups {
ownerId, id := "", name
if items := strings.Split(id, "/"); len(items) > 1 {
ownerId, id = items[0], items[1]
}
perm.UserIdGroupPairs[i] = &ec2.UserIdGroupPair{
GroupId: aws.String(id),
}
if ownerId != "" {
perm.UserIdGroupPairs[i].UserId = aws.String(ownerId)
}
if !vpc {
perm.UserIdGroupPairs[i].GroupId = nil
perm.UserIdGroupPairs[i].GroupName = aws.String(id)
}
}
}
if raw, ok := m["cidr_blocks"]; ok {
list := raw.([]interface{})
for _, v := range list {
perm.IpRanges = append(perm.IpRanges, &ec2.IpRange{CidrIp: aws.String(v.(string))})
}
}
provider/aws: Add support for IPv6 to aws_security_group (#12655) ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSSecurityGroup_' ✹ ✭ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/13 19:07:33 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSSecurityGroup_ -timeout 120m === RUN TestAccAWSSecurityGroup_importBasic --- PASS: TestAccAWSSecurityGroup_importBasic (48.67s) === RUN TestAccAWSSecurityGroup_importIpv6 --- PASS: TestAccAWSSecurityGroup_importIpv6 (51.71s) === RUN TestAccAWSSecurityGroup_importSelf --- PASS: TestAccAWSSecurityGroup_importSelf (58.46s) === RUN TestAccAWSSecurityGroup_importSourceSecurityGroup --- PASS: TestAccAWSSecurityGroup_importSourceSecurityGroup (65.99s) === RUN TestAccAWSSecurityGroup_basic --- PASS: TestAccAWSSecurityGroup_basic (62.18s) === RUN TestAccAWSSecurityGroup_ipv6 --- PASS: TestAccAWSSecurityGroup_ipv6 (47.38s) === RUN TestAccAWSSecurityGroup_tagsCreatedFirst --- PASS: TestAccAWSSecurityGroup_tagsCreatedFirst (30.48s) === RUN TestAccAWSSecurityGroup_namePrefix --- PASS: TestAccAWSSecurityGroup_namePrefix (16.90s) === RUN TestAccAWSSecurityGroup_self --- PASS: TestAccAWSSecurityGroup_self (45.36s) === RUN TestAccAWSSecurityGroup_vpc --- PASS: TestAccAWSSecurityGroup_vpc (45.76s) === RUN TestAccAWSSecurityGroup_vpcNegOneIngress --- PASS: TestAccAWSSecurityGroup_vpcNegOneIngress (44.50s) === RUN TestAccAWSSecurityGroup_vpcProtoNumIngress --- PASS: TestAccAWSSecurityGroup_vpcProtoNumIngress (45.01s) === RUN TestAccAWSSecurityGroup_MultiIngress --- PASS: TestAccAWSSecurityGroup_MultiIngress (50.67s) === RUN TestAccAWSSecurityGroup_Change --- PASS: TestAccAWSSecurityGroup_Change (76.34s) === RUN TestAccAWSSecurityGroup_generatedName --- PASS: TestAccAWSSecurityGroup_generatedName (46.29s) === RUN TestAccAWSSecurityGroup_DefaultEgress_VPC --- PASS: TestAccAWSSecurityGroup_DefaultEgress_VPC (48.10s) === RUN TestAccAWSSecurityGroup_DefaultEgress_Classic --- PASS: TestAccAWSSecurityGroup_DefaultEgress_Classic (16.71s) === RUN TestAccAWSSecurityGroup_drift --- PASS: TestAccAWSSecurityGroup_drift (22.73s) === RUN TestAccAWSSecurityGroup_drift_complex --- PASS: TestAccAWSSecurityGroup_drift_complex (54.14s) === RUN TestAccAWSSecurityGroup_tags --- PASS: TestAccAWSSecurityGroup_tags (74.12s) === RUN TestAccAWSSecurityGroup_CIDRandGroups --- PASS: TestAccAWSSecurityGroup_CIDRandGroups (55.83s) === RUN TestAccAWSSecurityGroup_ingressWithCidrAndSGs --- PASS: TestAccAWSSecurityGroup_ingressWithCidrAndSGs (54.40s) === RUN TestAccAWSSecurityGroup_ingressWithCidrAndSGs_classic --- PASS: TestAccAWSSecurityGroup_ingressWithCidrAndSGs_classic (26.93s) === RUN TestAccAWSSecurityGroup_egressWithPrefixList --- PASS: TestAccAWSSecurityGroup_egressWithPrefixList (61.39s) === RUN TestAccAWSSecurityGroup_failWithDiffMismatch --- PASS: TestAccAWSSecurityGroup_failWithDiffMismatch (55.93s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 1136.500s ```
2017-03-14 12:02:50 +01:00
if raw, ok := m["ipv6_cidr_blocks"]; ok {
list := raw.([]interface{})
for _, v := range list {
perm.Ipv6Ranges = append(perm.Ipv6Ranges, &ec2.Ipv6Range{CidrIpv6: aws.String(v.(string))})
}
}
if raw, ok := m["prefix_list_ids"]; ok {
list := raw.([]interface{})
for _, v := range list {
perm.PrefixListIds = append(perm.PrefixListIds, &ec2.PrefixListId{PrefixListId: aws.String(v.(string))})
}
}
2015-04-16 22:18:01 +02:00
perms[i] = &perm
}
return perms, nil
}
// Takes the result of flatmap.Expand for an array of parameters and
// returns Parameter API compatible objects
2015-04-16 22:28:18 +02:00
func expandParameters(configured []interface{}) ([]*rds.Parameter, error) {
var parameters []*rds.Parameter
// Loop over our configured parameters and create
2016-09-12 08:14:24 +02:00
// an array of aws-sdk-go compatible objects
for _, pRaw := range configured {
data := pRaw.(map[string]interface{})
if data["name"].(string) == "" {
continue
}
2015-04-16 22:18:01 +02:00
p := &rds.Parameter{
ApplyMethod: aws.String(data["apply_method"].(string)),
ParameterName: aws.String(data["name"].(string)),
ParameterValue: aws.String(data["value"].(string)),
}
parameters = append(parameters, p)
}
return parameters, nil
}
func expandRedshiftParameters(configured []interface{}) ([]*redshift.Parameter, error) {
var parameters []*redshift.Parameter
// Loop over our configured parameters and create
2016-09-12 08:14:24 +02:00
// an array of aws-sdk-go compatible objects
for _, pRaw := range configured {
data := pRaw.(map[string]interface{})
if data["name"].(string) == "" {
continue
}
p := &redshift.Parameter{
ParameterName: aws.String(data["name"].(string)),
ParameterValue: aws.String(data["value"].(string)),
}
parameters = append(parameters, p)
}
return parameters, nil
}
func expandOptionConfiguration(configured []interface{}) ([]*rds.OptionConfiguration, error) {
var option []*rds.OptionConfiguration
for _, pRaw := range configured {
data := pRaw.(map[string]interface{})
o := &rds.OptionConfiguration{
OptionName: aws.String(data["option_name"].(string)),
}
if raw, ok := data["port"]; ok {
port := raw.(int)
if port != 0 {
o.Port = aws.Int64(int64(port))
}
}
if raw, ok := data["db_security_group_memberships"]; ok {
memberships := expandStringList(raw.(*schema.Set).List())
if len(memberships) > 0 {
o.DBSecurityGroupMemberships = memberships
}
}
if raw, ok := data["vpc_security_group_memberships"]; ok {
memberships := expandStringList(raw.(*schema.Set).List())
if len(memberships) > 0 {
o.VpcSecurityGroupMemberships = memberships
}
}
if raw, ok := data["option_settings"]; ok {
o.OptionSettings = expandOptionSetting(raw.(*schema.Set).List())
}
option = append(option, o)
}
return option, nil
}
func expandOptionSetting(list []interface{}) []*rds.OptionSetting {
options := make([]*rds.OptionSetting, 0, len(list))
for _, oRaw := range list {
data := oRaw.(map[string]interface{})
o := &rds.OptionSetting{
Name: aws.String(data["name"].(string)),
Value: aws.String(data["value"].(string)),
}
options = append(options, o)
}
return options
}
// Takes the result of flatmap.Expand for an array of parameters and
// returns Parameter API compatible objects
func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.ParameterNameValue, error) {
parameters := make([]*elasticache.ParameterNameValue, 0, len(configured))
// Loop over our configured parameters and create
2016-09-12 08:14:24 +02:00
// an array of aws-sdk-go compatible objects
for _, pRaw := range configured {
data := pRaw.(map[string]interface{})
p := &elasticache.ParameterNameValue{
ParameterName: aws.String(data["name"].(string)),
ParameterValue: aws.String(data["value"].(string)),
}
parameters = append(parameters, p)
}
return parameters, nil
}
2015-10-30 23:45:43 +01:00
// Flattens an access log into something that flatmap.Flatten() can handle
func flattenAccessLog(l *elb.AccessLog) []map[string]interface{} {
2015-11-03 23:30:18 +01:00
result := make([]map[string]interface{}, 0, 1)
if l == nil {
return nil
}
r := make(map[string]interface{})
if l.S3BucketName != nil {
r["bucket"] = *l.S3BucketName
}
if l.S3BucketPrefix != nil {
r["bucket_prefix"] = *l.S3BucketPrefix
}
if l.EmitInterval != nil {
r["interval"] = *l.EmitInterval
}
if l.Enabled != nil {
r["enabled"] = *l.Enabled
2015-11-03 23:30:18 +01:00
}
result = append(result, r)
2015-11-03 23:30:18 +01:00
return result
2015-10-30 23:45:43 +01:00
}
// Takes the result of flatmap.Expand for an array of step adjustments and
// returns a []*autoscaling.StepAdjustment.
func expandStepAdjustments(configured []interface{}) ([]*autoscaling.StepAdjustment, error) {
var adjustments []*autoscaling.StepAdjustment
// Loop over our configured step adjustments and create an array
// of aws-sdk-go compatible objects. We're forced to convert strings
// to floats here because there's no way to detect whether or not
// an uninitialized, optional schema element is "0.0" deliberately.
// With strings, we can test for "", which is definitely an empty
// struct value.
for _, raw := range configured {
data := raw.(map[string]interface{})
a := &autoscaling.StepAdjustment{
ScalingAdjustment: aws.Int64(int64(data["scaling_adjustment"].(int))),
}
if data["metric_interval_lower_bound"] != "" {
bound := data["metric_interval_lower_bound"]
switch bound := bound.(type) {
case string:
f, err := strconv.ParseFloat(bound, 64)
if err != nil {
return nil, fmt.Errorf(
"metric_interval_lower_bound must be a float value represented as a string")
}
a.MetricIntervalLowerBound = aws.Float64(f)
default:
return nil, fmt.Errorf(
"metric_interval_lower_bound isn't a string. This is a bug. Please file an issue.")
}
}
if data["metric_interval_upper_bound"] != "" {
bound := data["metric_interval_upper_bound"]
switch bound := bound.(type) {
case string:
f, err := strconv.ParseFloat(bound, 64)
if err != nil {
return nil, fmt.Errorf(
"metric_interval_upper_bound must be a float value represented as a string")
}
a.MetricIntervalUpperBound = aws.Float64(f)
default:
return nil, fmt.Errorf(
"metric_interval_upper_bound isn't a string. This is a bug. Please file an issue.")
}
}
adjustments = append(adjustments, a)
}
return adjustments, nil
}
// Flattens a health check into something that flatmap.Flatten()
// can handle
2015-04-16 22:28:18 +02:00
func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} {
result := make([]map[string]interface{}, 0, 1)
chk := make(map[string]interface{})
chk["unhealthy_threshold"] = *check.UnhealthyThreshold
chk["healthy_threshold"] = *check.HealthyThreshold
chk["target"] = *check.Target
chk["timeout"] = *check.Timeout
chk["interval"] = *check.Interval
result = append(result, chk)
return result
}
2016-03-09 22:00:30 +01:00
// Flattens an array of UserSecurityGroups into a []*ec2.GroupIdentifier
func flattenSecurityGroups(list []*ec2.UserIdGroupPair, ownerId *string) []*ec2.GroupIdentifier {
result := make([]*ec2.GroupIdentifier, 0, len(list))
for _, g := range list {
var userId *string
if g.UserId != nil && *g.UserId != "" && (ownerId == nil || *ownerId != *g.UserId) {
userId = g.UserId
}
2016-03-09 22:50:01 +01:00
// userid nil here for same vpc groups
vpc := g.GroupName == nil || *g.GroupName == ""
var id *string
if vpc {
id = g.GroupId
} else {
id = g.GroupName
}
2016-03-09 22:50:01 +01:00
// id is groupid for vpcs
// id is groupname for non vpc (classic)
if userId != nil {
id = aws.String(*userId + "/" + *id)
}
if vpc {
result = append(result, &ec2.GroupIdentifier{
GroupId: id,
})
} else {
result = append(result, &ec2.GroupIdentifier{
GroupId: g.GroupId,
GroupName: id,
})
}
}
return result
}
2014-07-16 23:02:47 +02:00
// Flattens an array of Instances into a []string
2015-04-16 22:28:18 +02:00
func flattenInstances(list []*elb.Instance) []string {
2014-07-16 23:02:47 +02:00
result := make([]string, 0, len(list))
for _, i := range list {
result = append(result, *i.InstanceId)
}
return result
}
// Expands an array of String Instance IDs into a []Instances
2015-04-16 22:28:18 +02:00
func expandInstanceString(list []interface{}) []*elb.Instance {
2015-04-16 22:18:01 +02:00
result := make([]*elb.Instance, 0, len(list))
for _, i := range list {
result = append(result, &elb.Instance{InstanceId: aws.String(i.(string))})
2014-07-16 23:02:47 +02:00
}
return result
}
// Flattens an array of Backend Descriptions into a a map of instance_port to policy names.
func flattenBackendPolicies(backends []*elb.BackendServerDescription) map[int64][]string {
policies := make(map[int64][]string)
for _, i := range backends {
for _, p := range i.PolicyNames {
policies[*i.InstancePort] = append(policies[*i.InstancePort], *p)
}
sort.Strings(policies[*i.InstancePort])
}
return policies
}
// Flattens an array of Listeners into a []map[string]interface{}
2015-04-16 22:28:18 +02:00
func flattenListeners(list []*elb.ListenerDescription) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(list))
for _, i := range list {
l := map[string]interface{}{
"instance_port": *i.Listener.InstancePort,
"instance_protocol": strings.ToLower(*i.Listener.InstanceProtocol),
"lb_port": *i.Listener.LoadBalancerPort,
"lb_protocol": strings.ToLower(*i.Listener.Protocol),
}
// SSLCertificateID is optional, and may be nil
if i.Listener.SSLCertificateId != nil {
l["ssl_certificate_id"] = *i.Listener.SSLCertificateId
}
result = append(result, l)
}
return result
}
// Flattens an array of Volumes into a []map[string]interface{}
func flattenEcsVolumes(list []*ecs.Volume) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(list))
for _, volume := range list {
l := map[string]interface{}{
"name": *volume.Name,
}
if volume.Host.SourcePath != nil {
l["host_path"] = *volume.Host.SourcePath
}
result = append(result, l)
}
return result
}
// Flattens an array of ECS LoadBalancers into a []map[string]interface{}
func flattenEcsLoadBalancers(list []*ecs.LoadBalancer) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(list))
for _, loadBalancer := range list {
l := map[string]interface{}{
"container_name": *loadBalancer.ContainerName,
"container_port": *loadBalancer.ContainerPort,
}
if loadBalancer.LoadBalancerName != nil {
l["elb_name"] = *loadBalancer.LoadBalancerName
}
if loadBalancer.TargetGroupArn != nil {
l["target_group_arn"] = *loadBalancer.TargetGroupArn
}
result = append(result, l)
}
return result
}
// Encodes an array of ecs.ContainerDefinitions into a JSON string
func flattenEcsContainerDefinitions(definitions []*ecs.ContainerDefinition) (string, error) {
byteArray, err := json.Marshal(definitions)
if err != nil {
return "", fmt.Errorf("Error encoding to JSON: %s", err)
}
n := bytes.Index(byteArray, []byte{0})
return string(byteArray[:n]), nil
}
// Flattens an array of Options into a []map[string]interface{}
func flattenOptions(list []*rds.Option) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(list))
for _, i := range list {
if i.OptionName != nil {
r := make(map[string]interface{})
r["option_name"] = strings.ToLower(*i.OptionName)
// Default empty string, guard against nil parameter values
r["port"] = ""
if i.Port != nil {
r["port"] = int(*i.Port)
}
if i.VpcSecurityGroupMemberships != nil {
vpcs := make([]string, 0, len(i.VpcSecurityGroupMemberships))
for _, vpc := range i.VpcSecurityGroupMemberships {
id := vpc.VpcSecurityGroupId
vpcs = append(vpcs, *id)
}
r["vpc_security_group_memberships"] = vpcs
}
if i.DBSecurityGroupMemberships != nil {
dbs := make([]string, 0, len(i.DBSecurityGroupMemberships))
for _, db := range i.DBSecurityGroupMemberships {
id := db.DBSecurityGroupName
dbs = append(dbs, *id)
}
r["db_security_group_memberships"] = dbs
}
if i.OptionSettings != nil {
settings := make([]map[string]interface{}, 0, len(i.OptionSettings))
for _, j := range i.OptionSettings {
setting := map[string]interface{}{
"name": *j.Name,
}
if j.Value != nil {
setting["value"] = *j.Value
}
settings = append(settings, setting)
}
r["option_settings"] = settings
}
result = append(result, r)
}
}
return result
}
// Flattens an array of Parameters into a []map[string]interface{}
2015-04-16 22:28:18 +02:00
func flattenParameters(list []*rds.Parameter) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(list))
for _, i := range list {
if i.ParameterName != nil {
r := make(map[string]interface{})
r["name"] = strings.ToLower(*i.ParameterName)
// Default empty string, guard against nil parameter values
r["value"] = ""
if i.ParameterValue != nil {
r["value"] = strings.ToLower(*i.ParameterValue)
}
if i.ApplyMethod != nil {
r["apply_method"] = strings.ToLower(*i.ApplyMethod)
}
result = append(result, r)
}
}
return result
}
// Flattens an array of Redshift Parameters into a []map[string]interface{}
func flattenRedshiftParameters(list []*redshift.Parameter) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(list))
for _, i := range list {
result = append(result, map[string]interface{}{
"name": strings.ToLower(*i.ParameterName),
"value": strings.ToLower(*i.ParameterValue),
})
}
return result
}
// Flattens an array of Parameters into a []map[string]interface{}
func flattenElastiCacheParameters(list []*elasticache.Parameter) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(list))
for _, i := range list {
if i.ParameterValue != nil {
result = append(result, map[string]interface{}{
"name": strings.ToLower(*i.ParameterName),
"value": *i.ParameterValue,
})
}
}
return result
}
// Takes the result of flatmap.Expand for an array of strings
2015-09-16 00:11:53 +02:00
// and returns a []*string
2015-04-16 22:28:18 +02:00
func expandStringList(configured []interface{}) []*string {
2015-04-16 22:18:01 +02:00
vs := make([]*string, 0, len(configured))
for _, v := range configured {
val, ok := v.(string)
if ok && val != "" {
vs = append(vs, aws.String(v.(string)))
}
}
return vs
}
// Takes the result of schema.Set of strings and returns a []*string
func expandStringSet(configured *schema.Set) []*string {
return expandStringList(configured.List())
}
2015-09-16 00:11:53 +02:00
// Takes list of pointers to strings. Expand to an array
// of raw strings and returns a []interface{}
// to keep compatibility w/ schema.NewSetschema.NewSet
func flattenStringList(list []*string) []interface{} {
vs := make([]interface{}, 0, len(list))
for _, v := range list {
vs = append(vs, *v)
}
return vs
}
//Flattens an array of private ip addresses into a []string, where the elements returned are the IP strings e.g. "192.168.0.0"
2015-09-11 20:56:20 +02:00
func flattenNetworkInterfacesPrivateIPAddresses(dtos []*ec2.NetworkInterfacePrivateIpAddress) []string {
ips := make([]string, 0, len(dtos))
for _, v := range dtos {
ip := *v.PrivateIpAddress
ips = append(ips, ip)
}
return ips
}
//Flattens security group identifiers into a []string, where the elements returned are the GroupIDs
2015-04-16 22:28:18 +02:00
func flattenGroupIdentifiers(dtos []*ec2.GroupIdentifier) []string {
ids := make([]string, 0, len(dtos))
for _, v := range dtos {
group_id := *v.GroupId
ids = append(ids, group_id)
}
return ids
}
//Expands an array of IPs into a ec2 Private IP Address Spec
2015-09-11 20:56:20 +02:00
func expandPrivateIPAddresses(ips []interface{}) []*ec2.PrivateIpAddressSpecification {
dtos := make([]*ec2.PrivateIpAddressSpecification, 0, len(ips))
for i, v := range ips {
new_private_ip := &ec2.PrivateIpAddressSpecification{
PrivateIpAddress: aws.String(v.(string)),
}
new_private_ip.Primary = aws.Bool(i == 0)
dtos = append(dtos, new_private_ip)
}
return dtos
}
//Flattens network interface attachment into a map[string]interface
2015-04-16 22:28:18 +02:00
func flattenAttachment(a *ec2.NetworkInterfaceAttachment) map[string]interface{} {
att := make(map[string]interface{})
if a.InstanceId != nil {
att["instance"] = *a.InstanceId
}
att["device_index"] = *a.DeviceIndex
att["attachment_id"] = *a.AttachmentId
return att
}
2015-04-16 22:18:01 +02:00
provider/aws: Support Import of aws_elasticache_cluster Initial tests were failing as follows: ``` === RUN TestAccAWSElasticacheCluster_importBasic --- FAIL: TestAccAWSElasticacheCluster_importBasic (362.66s) testing.go:265: Step 1 error: ImportStateVerify attributes not equivalent. Difference is shown below. Top is actual, bottom is expected. (map[string]string) { } (map[string]string) (len=2) { (string) (len=20) "parameter_group_name": (string) (len=20) "default.memcached1.4", (string) (len=22) "security_group_names.#": (string) (len=1) "0" } FAIL exit status 1 ``` The import of ElastiCache clusters helped to point out 3 things: 1. Currently, we were trying to set the parameter_group_name as follows: ``` d.Set("parameter_group_name", c.CacheParameterGroup) ``` Unfortunately, c.CacheParameterGroup is a struct not a string. This was causing the test import failure. So this had to be replaced as follows: ``` if c.CacheParameterGroup != nil { d.Set("parameter_group_name", c.CacheParameterGroup.CacheParameterGroupName) } ``` 2. We were trying to set the security_group_names as follows: ``` d.Set("security_group_names", c.CacheSecurityGroups) ``` The CacheSecurityGroups was actually a []* so had to be changed to work as follows: ``` if len(c.CacheSecurityGroups) > 0 { d.Set("security_group_names", flattenElastiCacheSecurityGroupNames(c.CacheSecurityGroups)) } ``` 3. We were trying to set the security_group_ids as follows: ``` d.Set("security_group_ids", c.SecurityGroups) ``` This is another []* and needs to be changed as follows: ``` if len(c.SecurityGroups) > 0 { d.Set("security_group_ids", flattenElastiCacheSecurityGroupIds(c.SecurityGroups)) } ``` This then allows the import test to pass as expected: ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSElasticacheCluster_importBasic' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/09/23 10:59:01 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSElasticacheCluster_importBasic -timeout 120m === RUN TestAccAWSElasticacheCluster_importBasic --- PASS: TestAccAWSElasticacheCluster_importBasic (351.96s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 351.981s ``` As a final test, I ran the basic ElastiCache cluster creation to make sure all passed as expected: ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSElasticacheCluster_basic' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/09/23 11:05:51 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSElasticacheCluster_basic -timeout 120m === RUN TestAccAWSElasticacheCluster_basic --- PASS: TestAccAWSElasticacheCluster_basic (809.25s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 809.267s ```
2016-09-23 12:06:19 +02:00
func flattenElastiCacheSecurityGroupNames(securityGroups []*elasticache.CacheSecurityGroupMembership) []string {
result := make([]string, 0, len(securityGroups))
for _, sg := range securityGroups {
if sg.CacheSecurityGroupName != nil {
result = append(result, *sg.CacheSecurityGroupName)
}
}
return result
}
func flattenElastiCacheSecurityGroupIds(securityGroups []*elasticache.SecurityGroupMembership) []string {
result := make([]string, 0, len(securityGroups))
for _, sg := range securityGroups {
if sg.SecurityGroupId != nil {
result = append(result, *sg.SecurityGroupId)
}
}
return result
}
// Flattens step adjustments into a list of map[string]interface.
func flattenStepAdjustments(adjustments []*autoscaling.StepAdjustment) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(adjustments))
for _, raw := range adjustments {
a := map[string]interface{}{
"scaling_adjustment": *raw.ScalingAdjustment,
}
if raw.MetricIntervalUpperBound != nil {
a["metric_interval_upper_bound"] = *raw.MetricIntervalUpperBound
}
if raw.MetricIntervalLowerBound != nil {
a["metric_interval_lower_bound"] = *raw.MetricIntervalLowerBound
}
result = append(result, a)
}
return result
}
func flattenResourceRecords(recs []*route53.ResourceRecord, typeStr string) []string {
2015-04-16 22:18:01 +02:00
strs := make([]string, 0, len(recs))
for _, r := range recs {
if r.Value != nil {
s := *r.Value
if typeStr == "TXT" || typeStr == "SPF" {
s = expandTxtEntry(s)
}
2015-04-16 22:18:01 +02:00
strs = append(strs, s)
}
}
return strs
}
func expandResourceRecords(recs []interface{}, typeStr string) []*route53.ResourceRecord {
records := make([]*route53.ResourceRecord, 0, len(recs))
for _, r := range recs {
s := r.(string)
if typeStr == "TXT" || typeStr == "SPF" {
s = flattenTxtEntry(s)
2015-04-16 22:18:01 +02:00
}
records = append(records, &route53.ResourceRecord{Value: aws.String(s)})
2015-04-16 22:18:01 +02:00
}
return records
}
// How 'flattenTxtEntry' and 'expandTxtEntry' work.
//
// In the Route 53, TXT entries are written using quoted strings, one per line.
// Example:
// "x=foo"
// "bar=12"
//
// In Terraform, there are two differences:
// - We use a list of strings instead of separating strings with newlines.
// - Within each string, we dont' include the surrounding quotes.
// Example:
// records = ["x=foo", "bar=12"] # Instead of ["\"x=foo\", \"bar=12\""]
//
// When we pull from Route 53, `expandTxtEntry` removes the surrounding quotes;
// when we push to Route 53, `flattenTxtEntry` adds them back.
//
// One complication is that a single TXT entry can have multiple quoted strings.
// For example, here are two TXT entries, one with two quoted strings and the
// other with three.
// "x=" "foo"
// "ba" "r" "=12"
//
// DNS clients are expected to merge the quoted strings before interpreting the
// value. Since `expandTxtEntry` only removes the quotes at the end we can still
// (hackily) represent the above configuration in Terraform:
// records = ["x=\" \"foo", "ba\" \"r\" \"=12"]
//
// The primary reason to use multiple strings for an entry is that DNS (and Route
// 53) doesn't allow a quoted string to be more than 255 characters long. If you
// want a longer TXT entry, you must use multiple quoted strings.
//
// It would be nice if this Terraform automatically split strings longer than 255
// characters. For example, imagine "xxx..xxx" has 256 "x" characters.
// records = ["xxx..xxx"]
// When pushing to Route 53, this could be converted to:
// "xxx..xx" "x"
//
// This could also work when the user is already using multiple quoted strings:
// records = ["xxx.xxx\" \"yyy..yyy"]
// When pushing to Route 53, this could be converted to:
// "xxx..xx" "xyyy...y" "yy"
//
// If you want to add this feature, make sure to follow all the quoting rules in
// <https://tools.ietf.org/html/rfc1464#section-2>. If you make a mistake, people
// might end up relying on that mistake so fixing it would be a breaking change.
func flattenTxtEntry(s string) string {
return fmt.Sprintf(`"%s"`, s)
}
func expandTxtEntry(s string) string {
last := len(s) - 1
if last != 0 && s[0] == '"' && s[last] == '"' {
s = s[1:last]
}
return s
}
func expandESClusterConfig(m map[string]interface{}) *elasticsearch.ElasticsearchClusterConfig {
config := elasticsearch.ElasticsearchClusterConfig{}
if v, ok := m["dedicated_master_enabled"]; ok {
isEnabled := v.(bool)
config.DedicatedMasterEnabled = aws.Bool(isEnabled)
if isEnabled {
if v, ok := m["dedicated_master_count"]; ok && v.(int) > 0 {
config.DedicatedMasterCount = aws.Int64(int64(v.(int)))
}
if v, ok := m["dedicated_master_type"]; ok && v.(string) != "" {
config.DedicatedMasterType = aws.String(v.(string))
}
}
}
if v, ok := m["instance_count"]; ok {
config.InstanceCount = aws.Int64(int64(v.(int)))
}
if v, ok := m["instance_type"]; ok {
config.InstanceType = aws.String(v.(string))
}
if v, ok := m["zone_awareness_enabled"]; ok {
config.ZoneAwarenessEnabled = aws.Bool(v.(bool))
}
return &config
}
func flattenESClusterConfig(c *elasticsearch.ElasticsearchClusterConfig) []map[string]interface{} {
m := map[string]interface{}{}
if c.DedicatedMasterCount != nil {
m["dedicated_master_count"] = *c.DedicatedMasterCount
}
if c.DedicatedMasterEnabled != nil {
m["dedicated_master_enabled"] = *c.DedicatedMasterEnabled
}
if c.DedicatedMasterType != nil {
m["dedicated_master_type"] = *c.DedicatedMasterType
}
if c.InstanceCount != nil {
m["instance_count"] = *c.InstanceCount
}
if c.InstanceType != nil {
m["instance_type"] = *c.InstanceType
}
if c.ZoneAwarenessEnabled != nil {
m["zone_awareness_enabled"] = *c.ZoneAwarenessEnabled
}
return []map[string]interface{}{m}
}
func flattenESEBSOptions(o *elasticsearch.EBSOptions) []map[string]interface{} {
m := map[string]interface{}{}
if o.EBSEnabled != nil {
m["ebs_enabled"] = *o.EBSEnabled
}
if o.Iops != nil {
m["iops"] = *o.Iops
}
if o.VolumeSize != nil {
m["volume_size"] = *o.VolumeSize
}
if o.VolumeType != nil {
m["volume_type"] = *o.VolumeType
}
return []map[string]interface{}{m}
}
func expandESEBSOptions(m map[string]interface{}) *elasticsearch.EBSOptions {
options := elasticsearch.EBSOptions{}
if v, ok := m["ebs_enabled"]; ok {
options.EBSEnabled = aws.Bool(v.(bool))
}
if v, ok := m["iops"]; ok && v.(int) > 0 {
options.Iops = aws.Int64(int64(v.(int)))
}
if v, ok := m["volume_size"]; ok && v.(int) > 0 {
options.VolumeSize = aws.Int64(int64(v.(int)))
}
if v, ok := m["volume_type"]; ok && v.(string) != "" {
options.VolumeType = aws.String(v.(string))
}
return &options
}
func expandConfigRecordingGroup(configured []interface{}) *configservice.RecordingGroup {
recordingGroup := configservice.RecordingGroup{}
group := configured[0].(map[string]interface{})
if v, ok := group["all_supported"]; ok {
recordingGroup.AllSupported = aws.Bool(v.(bool))
}
if v, ok := group["include_global_resource_types"]; ok {
recordingGroup.IncludeGlobalResourceTypes = aws.Bool(v.(bool))
}
if v, ok := group["resource_types"]; ok {
recordingGroup.ResourceTypes = expandStringList(v.(*schema.Set).List())
}
return &recordingGroup
}
func flattenConfigRecordingGroup(g *configservice.RecordingGroup) []map[string]interface{} {
m := make(map[string]interface{}, 1)
if g.AllSupported != nil {
m["all_supported"] = *g.AllSupported
}
if g.IncludeGlobalResourceTypes != nil {
m["include_global_resource_types"] = *g.IncludeGlobalResourceTypes
}
if g.ResourceTypes != nil && len(g.ResourceTypes) > 0 {
m["resource_types"] = schema.NewSet(schema.HashString, flattenStringList(g.ResourceTypes))
}
return []map[string]interface{}{m}
}
func flattenConfigSnapshotDeliveryProperties(p *configservice.ConfigSnapshotDeliveryProperties) []map[string]interface{} {
m := make(map[string]interface{}, 0)
if p.DeliveryFrequency != nil {
m["delivery_frequency"] = *p.DeliveryFrequency
}
return []map[string]interface{}{m}
}
func pointersMapToStringList(pointers map[string]*string) map[string]interface{} {
list := make(map[string]interface{}, len(pointers))
for i, v := range pointers {
list[i] = *v
}
return list
}
func stringMapToPointers(m map[string]interface{}) map[string]*string {
list := make(map[string]*string, len(m))
for i, v := range m {
list[i] = aws.String(v.(string))
}
return list
}
func flattenDSVpcSettings(
s *directoryservice.DirectoryVpcSettingsDescription) []map[string]interface{} {
settings := make(map[string]interface{}, 0)
if s == nil {
return nil
}
settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds))
settings["vpc_id"] = *s.VpcId
return []map[string]interface{}{settings}
}
func flattenLambdaEnvironment(lambdaEnv *lambda.EnvironmentResponse) []interface{} {
envs := make(map[string]interface{})
en := make(map[string]string)
if lambdaEnv == nil {
return nil
}
for k, v := range lambdaEnv.Variables {
en[k] = *v
}
if len(en) > 0 {
envs["variables"] = en
}
return []interface{}{envs}
}
func flattenLambdaVpcConfigResponse(s *lambda.VpcConfigResponse) []map[string]interface{} {
settings := make(map[string]interface{}, 0)
if s == nil {
return nil
}
var emptyVpc bool
if s.VpcId == nil || *s.VpcId == "" {
emptyVpc = true
}
if len(s.SubnetIds) == 0 && len(s.SecurityGroupIds) == 0 && emptyVpc {
2016-02-23 21:56:46 +01:00
return nil
}
settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds))
settings["security_group_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SecurityGroupIds))
2016-02-18 22:45:32 +01:00
if s.VpcId != nil {
settings["vpc_id"] = *s.VpcId
}
return []map[string]interface{}{settings}
}
func flattenDSConnectSettings(
customerDnsIps []*string,
s *directoryservice.DirectoryConnectSettingsDescription) []map[string]interface{} {
if s == nil {
return nil
}
settings := make(map[string]interface{}, 0)
settings["customer_dns_ips"] = schema.NewSet(schema.HashString, flattenStringList(customerDnsIps))
settings["connect_ips"] = schema.NewSet(schema.HashString, flattenStringList(s.ConnectIps))
settings["customer_username"] = *s.CustomerUserName
settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds))
settings["vpc_id"] = *s.VpcId
return []map[string]interface{}{settings}
}
func expandCloudFormationParameters(params map[string]interface{}) []*cloudformation.Parameter {
var cfParams []*cloudformation.Parameter
for k, v := range params {
cfParams = append(cfParams, &cloudformation.Parameter{
ParameterKey: aws.String(k),
ParameterValue: aws.String(v.(string)),
})
}
return cfParams
}
// flattenCloudFormationParameters is flattening list of
// *cloudformation.Parameters and only returning existing
// parameters to avoid clash with default values
func flattenCloudFormationParameters(cfParams []*cloudformation.Parameter,
originalParams map[string]interface{}) map[string]interface{} {
params := make(map[string]interface{}, len(cfParams))
for _, p := range cfParams {
_, isConfigured := originalParams[*p.ParameterKey]
if isConfigured {
params[*p.ParameterKey] = *p.ParameterValue
}
}
return params
}
func flattenAllCloudFormationParameters(cfParams []*cloudformation.Parameter) map[string]interface{} {
params := make(map[string]interface{}, len(cfParams))
for _, p := range cfParams {
params[*p.ParameterKey] = *p.ParameterValue
}
return params
}
func expandCloudFormationTags(tags map[string]interface{}) []*cloudformation.Tag {
var cfTags []*cloudformation.Tag
for k, v := range tags {
cfTags = append(cfTags, &cloudformation.Tag{
Key: aws.String(k),
Value: aws.String(v.(string)),
})
}
return cfTags
}
func flattenCloudFormationTags(cfTags []*cloudformation.Tag) map[string]string {
tags := make(map[string]string, len(cfTags))
for _, t := range cfTags {
tags[*t.Key] = *t.Value
}
return tags
}
func flattenCloudFormationOutputs(cfOutputs []*cloudformation.Output) map[string]string {
outputs := make(map[string]string, len(cfOutputs))
for _, o := range cfOutputs {
outputs[*o.OutputKey] = *o.OutputValue
}
return outputs
}
func flattenAsgSuspendedProcesses(list []*autoscaling.SuspendedProcess) []string {
strs := make([]string, 0, len(list))
for _, r := range list {
if r.ProcessName != nil {
strs = append(strs, *r.ProcessName)
}
}
return strs
}
func flattenAsgEnabledMetrics(list []*autoscaling.EnabledMetric) []string {
strs := make([]string, 0, len(list))
for _, r := range list {
if r.Metric != nil {
strs = append(strs, *r.Metric)
}
}
return strs
}
func flattenKinesisShardLevelMetrics(list []*kinesis.EnhancedMetrics) []string {
if len(list) == 0 {
return []string{}
}
strs := make([]string, 0, len(list[0].ShardLevelMetrics))
for _, s := range list[0].ShardLevelMetrics {
strs = append(strs, *s)
}
return strs
}
func flattenApiGatewayStageKeys(keys []*string) []map[string]interface{} {
2016-07-11 23:10:13 +02:00
stageKeys := make([]map[string]interface{}, 0, len(keys))
for _, o := range keys {
key := make(map[string]interface{})
parts := strings.Split(*o, "/")
key["stage_name"] = parts[1]
key["rest_api_id"] = parts[0]
stageKeys = append(stageKeys, key)
}
return stageKeys
}
func expandApiGatewayStageKeys(d *schema.ResourceData) []*apigateway.StageKey {
var stageKeys []*apigateway.StageKey
if stageKeyData, ok := d.GetOk("stage_key"); ok {
params := stageKeyData.(*schema.Set).List()
for k := range params {
data := params[k].(map[string]interface{})
stageKeys = append(stageKeys, &apigateway.StageKey{
RestApiId: aws.String(data["rest_api_id"].(string)),
StageName: aws.String(data["stage_name"].(string)),
})
}
}
return stageKeys
}
func expandApiGatewayRequestResponseModelOperations(d *schema.ResourceData, key string, prefix string) []*apigateway.PatchOperation {
operations := make([]*apigateway.PatchOperation, 0)
oldModels, newModels := d.GetChange(key)
oldModelMap := oldModels.(map[string]interface{})
newModelMap := newModels.(map[string]interface{})
for k, _ := range oldModelMap {
operation := apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
}
for nK, nV := range newModelMap {
if nK == k {
operation.Op = aws.String("replace")
operation.Value = aws.String(nV.(string))
}
}
operations = append(operations, &operation)
}
for nK, nV := range newModelMap {
exists := false
for k, _ := range oldModelMap {
if k == nK {
exists = true
}
}
if !exists {
operation := apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(nK, "/", "~1", -1))),
Value: aws.String(nV.(string)),
}
operations = append(operations, &operation)
}
}
return operations
}
func deprecatedExpandApiGatewayMethodParametersJSONOperations(d *schema.ResourceData, key string, prefix string) ([]*apigateway.PatchOperation, error) {
operations := make([]*apigateway.PatchOperation, 0)
oldParameters, newParameters := d.GetChange(key)
oldParametersMap := make(map[string]interface{})
newParametersMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(oldParameters.(string)), &oldParametersMap); err != nil {
err := fmt.Errorf("Error unmarshaling old %s: %s", key, err)
return operations, err
}
if err := json.Unmarshal([]byte(newParameters.(string)), &newParametersMap); err != nil {
err := fmt.Errorf("Error unmarshaling new %s: %s", key, err)
return operations, err
}
for k, _ := range oldParametersMap {
operation := apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, k)),
}
for nK, nV := range newParametersMap {
if nK == k {
operation.Op = aws.String("replace")
operation.Value = aws.String(strconv.FormatBool(nV.(bool)))
}
}
operations = append(operations, &operation)
}
for nK, nV := range newParametersMap {
exists := false
for k, _ := range oldParametersMap {
if k == nK {
exists = true
}
}
if !exists {
operation := apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, nK)),
Value: aws.String(strconv.FormatBool(nV.(bool))),
}
operations = append(operations, &operation)
}
}
return operations, nil
}
func expandApiGatewayMethodParametersOperations(d *schema.ResourceData, key string, prefix string) ([]*apigateway.PatchOperation, error) {
operations := make([]*apigateway.PatchOperation, 0)
oldParameters, newParameters := d.GetChange(key)
oldParametersMap := oldParameters.(map[string]interface{})
newParametersMap := newParameters.(map[string]interface{})
for k, _ := range oldParametersMap {
operation := apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, k)),
}
for nK, nV := range newParametersMap {
b, ok := nV.(bool)
if !ok {
value, _ := strconv.ParseBool(nV.(string))
b = value
}
if nK == k {
operation.Op = aws.String("replace")
operation.Value = aws.String(strconv.FormatBool(b))
}
}
operations = append(operations, &operation)
}
for nK, nV := range newParametersMap {
exists := false
for k, _ := range oldParametersMap {
if k == nK {
exists = true
}
}
if !exists {
b, ok := nV.(bool)
if !ok {
value, _ := strconv.ParseBool(nV.(string))
b = value
}
operation := apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, nK)),
Value: aws.String(strconv.FormatBool(b)),
}
operations = append(operations, &operation)
}
}
return operations, nil
}
func expandApiGatewayStageKeyOperations(d *schema.ResourceData) []*apigateway.PatchOperation {
operations := make([]*apigateway.PatchOperation, 0)
prev, curr := d.GetChange("stage_key")
prevList := prev.(*schema.Set).List()
currList := curr.(*schema.Set).List()
for i := range prevList {
p := prevList[i].(map[string]interface{})
exists := false
for j := range currList {
c := currList[j].(map[string]interface{})
if c["rest_api_id"].(string) == p["rest_api_id"].(string) && c["stage_name"].(string) == p["stage_name"].(string) {
exists = true
}
}
if !exists {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String("/stages"),
Value: aws.String(fmt.Sprintf("%s/%s", p["rest_api_id"].(string), p["stage_name"].(string))),
})
}
}
for i := range currList {
c := currList[i].(map[string]interface{})
exists := false
for j := range prevList {
p := prevList[j].(map[string]interface{})
if c["rest_api_id"].(string) == p["rest_api_id"].(string) && c["stage_name"].(string) == p["stage_name"].(string) {
exists = true
}
}
if !exists {
operations = append(operations, &apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String("/stages"),
Value: aws.String(fmt.Sprintf("%s/%s", c["rest_api_id"].(string), c["stage_name"].(string))),
})
}
}
return operations
}
func expandCloudWachLogMetricTransformations(m map[string]interface{}) []*cloudwatchlogs.MetricTransformation {
transformation := cloudwatchlogs.MetricTransformation{
MetricName: aws.String(m["name"].(string)),
MetricNamespace: aws.String(m["namespace"].(string)),
MetricValue: aws.String(m["value"].(string)),
}
return []*cloudwatchlogs.MetricTransformation{&transformation}
}
func flattenCloudWachLogMetricTransformations(ts []*cloudwatchlogs.MetricTransformation) map[string]string {
m := make(map[string]string, 0)
m["name"] = *ts[0].MetricName
m["namespace"] = *ts[0].MetricNamespace
m["value"] = *ts[0].MetricValue
return m
}
func flattenBeanstalkAsg(list []*elasticbeanstalk.AutoScalingGroup) []string {
strs := make([]string, 0, len(list))
for _, r := range list {
if r.Name != nil {
strs = append(strs, *r.Name)
}
}
return strs
}
func flattenBeanstalkInstances(list []*elasticbeanstalk.Instance) []string {
strs := make([]string, 0, len(list))
for _, r := range list {
if r.Id != nil {
strs = append(strs, *r.Id)
}
}
return strs
}
func flattenBeanstalkLc(list []*elasticbeanstalk.LaunchConfiguration) []string {
strs := make([]string, 0, len(list))
for _, r := range list {
if r.Name != nil {
strs = append(strs, *r.Name)
}
}
return strs
}
func flattenBeanstalkElb(list []*elasticbeanstalk.LoadBalancer) []string {
strs := make([]string, 0, len(list))
for _, r := range list {
if r.Name != nil {
strs = append(strs, *r.Name)
}
}
return strs
}
func flattenBeanstalkSqs(list []*elasticbeanstalk.Queue) []string {
strs := make([]string, 0, len(list))
for _, r := range list {
if r.URL != nil {
strs = append(strs, *r.URL)
}
}
return strs
}
func flattenBeanstalkTrigger(list []*elasticbeanstalk.Trigger) []string {
strs := make([]string, 0, len(list))
for _, r := range list {
if r.Name != nil {
strs = append(strs, *r.Name)
}
}
return strs
}
// There are several parts of the AWS API that will sort lists of strings,
2016-09-12 08:14:24 +02:00
// causing diffs inbetween resources that use lists. This avoids a bit of
// code duplication for pre-sorts that can be used for things like hash
// functions, etc.
func sortInterfaceSlice(in []interface{}) []interface{} {
a := []string{}
b := []interface{}{}
for _, v := range in {
a = append(a, v.(string))
}
sort.Strings(a)
for _, v := range a {
b = append(b, v)
}
return b
}
2017-03-27 19:25:43 +02:00
// This function sorts List A to look like a list found in the tf file.
func sortListBasedonTFFile(in []string, d *schema.ResourceData, listName string) ([]string, error) {
if attributeCount, ok := d.Get(listName + ".#").(int); ok {
for i := 0; i < attributeCount; i++ {
currAttributeId := d.Get(listName + "." + strconv.Itoa(i))
for j := 0; j < len(in); j++ {
if currAttributeId == in[j] {
in[i], in[j] = in[j], in[i]
}
}
}
return in, nil
}
return in, fmt.Errorf("Could not find list: %s", listName)
}
func flattenApiGatewayThrottleSettings(settings *apigateway.ThrottleSettings) []map[string]interface{} {
result := make([]map[string]interface{}, 0, 1)
if settings != nil {
r := make(map[string]interface{})
if settings.BurstLimit != nil {
r["burst_limit"] = *settings.BurstLimit
}
if settings.RateLimit != nil {
r["rate_limit"] = *settings.RateLimit
}
result = append(result, r)
}
return result
}
// TODO: refactor some of these helper functions and types in the terraform/helper packages
// getStringPtr returns a *string version of the value taken from m, where m
// can be a map[string]interface{} or a *schema.ResourceData. If the key isn't
// present or is empty, getNilString returns nil.
func getStringPtr(m interface{}, key string) *string {
switch m := m.(type) {
case map[string]interface{}:
v := m[key]
if v == nil {
return nil
}
s := v.(string)
if s == "" {
return nil
}
return &s
case *schema.ResourceData:
if v, ok := m.GetOk(key); ok {
if v == nil || v.(string) == "" {
return nil
}
s := v.(string)
return &s
}
default:
panic("unknown type in getStringPtr")
}
return nil
}
// getStringPtrList returns a []*string version of the map value. If the key
// isn't present, getNilStringList returns nil.
func getStringPtrList(m map[string]interface{}, key string) []*string {
if v, ok := m[key]; ok {
var stringList []*string
for _, i := range v.([]interface{}) {
s := i.(string)
stringList = append(stringList, &s)
}
return stringList
}
return nil
}
// a convenience wrapper type for the schema.Set map[string]interface{}
// Set operations only alter the underlying map if the value is not nil
type setMap map[string]interface{}
// SetString sets m[key] = *value only if `value != nil`
func (s setMap) SetString(key string, value *string) {
if value == nil {
return
}
s[key] = *value
}
// SetStringMap sets key to value as a map[string]interface{}, stripping any nil
// values. The value parameter can be a map[string]interface{}, a
// map[string]*string, or a map[string]string.
func (s setMap) SetStringMap(key string, value interface{}) {
// because these methods are meant to be chained without intermediate
// checks for nil, we are likely to get interfaces with dynamic types but
// a nil value.
if reflect.ValueOf(value).IsNil() {
return
}
m := make(map[string]interface{})
switch value := value.(type) {
case map[string]string:
for k, v := range value {
m[k] = v
}
case map[string]*string:
for k, v := range value {
if v == nil {
continue
}
m[k] = *v
}
case map[string]interface{}:
for k, v := range value {
if v == nil {
continue
}
switch v := v.(type) {
case string:
m[k] = v
case *string:
if v != nil {
m[k] = *v
}
default:
panic(fmt.Sprintf("unknown type for SetString: %T", v))
}
}
}
// catch the case where the interface wasn't nil, but we had no non-nil values
if len(m) > 0 {
s[key] = m
}
}
// Set assigns value to s[key] if value isn't nil
func (s setMap) Set(key string, value interface{}) {
if reflect.ValueOf(value).IsNil() {
return
}
s[key] = value
}
// Map returns the raw map type for a shorter type conversion
func (s setMap) Map() map[string]interface{} {
return map[string]interface{}(s)
}
// MapList returns the map[string]interface{} as a single element in a slice to
// match the schema.Set data type used for structs.
func (s setMap) MapList() []map[string]interface{} {
return []map[string]interface{}{s.Map()}
}
// Takes the result of flatmap.Expand for an array of policy attributes and
// returns ELB API compatible objects
func expandPolicyAttributes(configured []interface{}) ([]*elb.PolicyAttribute, error) {
attributes := make([]*elb.PolicyAttribute, 0, len(configured))
// Loop over our configured attributes and create
// an array of aws-sdk-go compatible objects
for _, lRaw := range configured {
data := lRaw.(map[string]interface{})
a := &elb.PolicyAttribute{
AttributeName: aws.String(data["name"].(string)),
AttributeValue: aws.String(data["value"].(string)),
}
attributes = append(attributes, a)
}
return attributes, nil
}
// Flattens an array of PolicyAttributes into a []interface{}
func flattenPolicyAttributes(list []*elb.PolicyAttributeDescription) []interface{} {
attributes := []interface{}{}
for _, attrdef := range list {
attribute := map[string]string{
"name": *attrdef.AttributeName,
"value": *attrdef.AttributeValue,
}
attributes = append(attributes, attribute)
}
return attributes
}
func flattenConfigRuleSource(source *configservice.Source) []interface{} {
var result []interface{}
m := make(map[string]interface{})
m["owner"] = *source.Owner
m["source_identifier"] = *source.SourceIdentifier
if len(source.SourceDetails) > 0 {
m["source_detail"] = schema.NewSet(configRuleSourceDetailsHash, flattenConfigRuleSourceDetails(source.SourceDetails))
}
result = append(result, m)
return result
}
func flattenConfigRuleSourceDetails(details []*configservice.SourceDetail) []interface{} {
var items []interface{}
for _, d := range details {
m := make(map[string]interface{})
if d.MessageType != nil {
m["message_type"] = *d.MessageType
}
if d.EventSource != nil {
m["event_source"] = *d.EventSource
}
if d.MaximumExecutionFrequency != nil {
m["maximum_execution_frequency"] = *d.MaximumExecutionFrequency
}
items = append(items, m)
}
return items
}
func expandConfigRuleSource(configured []interface{}) *configservice.Source {
cfg := configured[0].(map[string]interface{})
source := configservice.Source{
Owner: aws.String(cfg["owner"].(string)),
SourceIdentifier: aws.String(cfg["source_identifier"].(string)),
}
if details, ok := cfg["source_detail"]; ok {
source.SourceDetails = expandConfigRuleSourceDetails(details.(*schema.Set))
}
return &source
}
func expandConfigRuleSourceDetails(configured *schema.Set) []*configservice.SourceDetail {
var results []*configservice.SourceDetail
for _, item := range configured.List() {
detail := item.(map[string]interface{})
src := configservice.SourceDetail{}
if msgType, ok := detail["message_type"].(string); ok && msgType != "" {
src.MessageType = aws.String(msgType)
}
if eventSource, ok := detail["event_source"].(string); ok && eventSource != "" {
src.EventSource = aws.String(eventSource)
}
if maxExecFreq, ok := detail["maximum_execution_frequency"].(string); ok && maxExecFreq != "" {
src.MaximumExecutionFrequency = aws.String(maxExecFreq)
}
results = append(results, &src)
}
return results
}
func flattenConfigRuleScope(scope *configservice.Scope) []interface{} {
var items []interface{}
m := make(map[string]interface{})
if scope.ComplianceResourceId != nil {
m["compliance_resource_id"] = *scope.ComplianceResourceId
}
if scope.ComplianceResourceTypes != nil {
m["compliance_resource_types"] = schema.NewSet(schema.HashString, flattenStringList(scope.ComplianceResourceTypes))
}
if scope.TagKey != nil {
m["tag_key"] = *scope.TagKey
}
if scope.TagValue != nil {
m["tag_value"] = *scope.TagValue
}
items = append(items, m)
return items
}
func expandConfigRuleScope(configured map[string]interface{}) *configservice.Scope {
scope := &configservice.Scope{}
if v, ok := configured["compliance_resource_id"].(string); ok && v != "" {
scope.ComplianceResourceId = aws.String(v)
}
if v, ok := configured["compliance_resource_types"]; ok {
l := v.(*schema.Set)
if l.Len() > 0 {
scope.ComplianceResourceTypes = expandStringList(l.List())
}
}
if v, ok := configured["tag_key"].(string); ok && v != "" {
scope.TagKey = aws.String(v)
}
if v, ok := configured["tag_value"].(string); ok && v != "" {
scope.TagValue = aws.String(v)
}
return scope
}
// Takes a value containing JSON string and passes it through
// the JSON parser to normalize it, returns either a parsing
// error or normalized JSON string.
func normalizeJsonString(jsonString interface{}) (string, error) {
var j interface{}
if jsonString == nil || jsonString.(string) == "" {
return "", nil
}
s := jsonString.(string)
err := json.Unmarshal([]byte(s), &j)
if err != nil {
return s, err
}
2017-05-19 20:36:39 +02:00
// The error is intentionally ignored here to allow empty policies to passthrough validation.
// This covers any interpolated values
bytes, _ := json.Marshal(j)
provider/aws: validation: Add validation function for IAM Policies The previous JSON validator that we were using for IAM policy documents wouldn't catch AWS IAM Policy errors. The supplied policy document would pass our validator, then fail with the following API error: ``` * aws_iam_role_policy.foo: Error putting IAM role policy tf_test_policy_ymw7hbil9w: MalformedPolicyDocument: The policy failed legacy parsing status code: 400, request id: e7615d90-3c99-11e7-babc-c14e741605bf ``` This happens if the Policy Document doesn't start with the opening JSON bracket, and often happens in the following case: ``` policy = <<EOF { "Version": "2012-10-17", "Statement": [ { ... } ] } EOF ``` Where, when using a HEREDOC, the policy document is indented incorrectly. The new validation function for the IAM policies verifies that the first character of the supplied policy document is the leading JSON bracket, prior to validating the JSON string. Test Output: ``` $ make test TEST=./builtin/providers/aws/ TESTARGS="-v -run=TestValidateIAMPolicyJsonString" ==> Checking that code complies with gofmt requirements... ==> Checking AWS provider for unchecked errors... ==> NOTE: at this time we only look for uncheck errors in the AWS package go generate $(go list ./... | grep -v /terraform/vendor/) 2017/05/19 10:56:32 Generated command/internal_plugin_list.go go test -i ./builtin/providers/aws/ || exit 1 echo ./builtin/providers/aws/ | \ xargs -t -n4 go test -v -run=TestValidateIAMPolicyJsonString -timeout=60s -parallel=4 go test -v -run=TestValidateIAMPolicyJsonString -timeout=60s -parallel=4 ./builtin/providers/aws/ === RUN TestValidateIAMPolicyJsonString --- PASS: TestValidateIAMPolicyJsonString (0.00s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 0.009s ``` ``` $ make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAWSPolicy_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/05/19 10:38:43 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAWSPolicy_ -timeout 120m === RUN TestAWSPolicy_namePrefix --- PASS: TestAWSPolicy_namePrefix (20.01s) === RUN TestAWSPolicy_invalidJson --- PASS: TestAWSPolicy_invalidJson (0.00s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 20.027s ``` ``` $ make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSIAMRolePolicy_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/05/19 11:02:56 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSIAMRolePolicy_ -timeout 120m === RUN TestAccAWSIAMRolePolicy_importBasic --- PASS: TestAccAWSIAMRolePolicy_importBasic (18.45s) === RUN TestAccAWSIAMRolePolicy_basic --- PASS: TestAccAWSIAMRolePolicy_basic (35.92s) === RUN TestAccAWSIAMRolePolicy_namePrefix --- PASS: TestAccAWSIAMRolePolicy_namePrefix (14.78s) === RUN TestAccAWSIAMRolePolicy_generatedName --- PASS: TestAccAWSIAMRolePolicy_generatedName (20.20s) === RUN TestAccAWSIAMRolePolicy_invalidJSON --- PASS: TestAccAWSIAMRolePolicy_invalidJSON (0.00s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 89.363s ```
2017-05-19 17:11:44 +02:00
return string(bytes[:]), nil
}
// Takes a value containing YAML string and passes it through
// the YAML parser. Returns either a parsing
// error or original YAML string.
func checkYamlString(yamlString interface{}) (string, error) {
var y interface{}
if yamlString == nil || yamlString.(string) == "" {
return "", nil
}
s := yamlString.(string)
err := yaml.Unmarshal([]byte(s), &y)
if err != nil {
return s, err
}
return s, nil
}
func normalizeCloudFormationTemplate(templateString interface{}) (string, error) {
if looksLikeJsonString(templateString) {
return normalizeJsonString(templateString)
} else {
return checkYamlString(templateString)
}
}
func flattenInspectorTags(cfTags []*cloudformation.Tag) map[string]string {
tags := make(map[string]string, len(cfTags))
for _, t := range cfTags {
tags[*t.Key] = *t.Value
}
return tags
}
func flattenApiGatewayUsageApiStages(s []*apigateway.ApiStage) []map[string]interface{} {
stages := make([]map[string]interface{}, 0)
for _, bd := range s {
if bd.ApiId != nil && bd.Stage != nil {
stage := make(map[string]interface{})
stage["api_id"] = *bd.ApiId
stage["stage"] = *bd.Stage
stages = append(stages, stage)
}
}
if len(stages) > 0 {
return stages
}
return nil
}
func flattenApiGatewayUsagePlanThrottling(s *apigateway.ThrottleSettings) []map[string]interface{} {
settings := make(map[string]interface{}, 0)
if s == nil {
return nil
}
if s.BurstLimit != nil {
settings["burst_limit"] = *s.BurstLimit
}
if s.RateLimit != nil {
settings["rate_limit"] = *s.RateLimit
}
return []map[string]interface{}{settings}
}
func flattenApiGatewayUsagePlanQuota(s *apigateway.QuotaSettings) []map[string]interface{} {
settings := make(map[string]interface{}, 0)
if s == nil {
return nil
}
if s.Limit != nil {
settings["limit"] = *s.Limit
}
if s.Offset != nil {
settings["offset"] = *s.Offset
}
if s.Period != nil {
settings["period"] = *s.Period
}
return []map[string]interface{}{settings}
}
func buildApiGatewayInvokeURL(restApiId, region, stageName string) string {
return fmt.Sprintf("https://%s.execute-api.%s.amazonaws.com/%s",
restApiId, region, stageName)
}
func buildApiGatewayExecutionARN(restApiId, region, accountId string) (string, error) {
if accountId == "" {
return "", fmt.Errorf("Unable to build execution ARN for %s as account ID is missing",
restApiId)
}
return fmt.Sprintf("arn:aws:execute-api:%s:%s:%s",
region, accountId, restApiId), nil
}
func expandCognitoSupportedLoginProviders(config map[string]interface{}) map[string]*string {
m := map[string]*string{}
for k, v := range config {
s := v.(string)
m[k] = &s
}
return m
}
func flattenCognitoSupportedLoginProviders(config map[string]*string) map[string]string {
m := map[string]string{}
for k, v := range config {
m[k] = *v
}
return m
}
func expandCognitoIdentityProviders(s *schema.Set) []*cognitoidentity.Provider {
ips := make([]*cognitoidentity.Provider, 0)
for _, v := range s.List() {
s := v.(map[string]interface{})
ip := &cognitoidentity.Provider{}
if sv, ok := s["client_id"].(string); ok {
ip.ClientId = aws.String(sv)
}
if sv, ok := s["provider_name"].(string); ok {
ip.ProviderName = aws.String(sv)
}
if sv, ok := s["server_side_token_check"].(bool); ok {
ip.ServerSideTokenCheck = aws.Bool(sv)
}
ips = append(ips, ip)
}
return ips
}
func flattenCognitoIdentityProviders(ips []*cognitoidentity.Provider) []map[string]interface{} {
values := make([]map[string]interface{}, 0)
for _, v := range ips {
ip := make(map[string]interface{})
if v == nil {
return nil
}
if v.ClientId != nil {
ip["client_id"] = *v.ClientId
}
if v.ProviderName != nil {
ip["provider_name"] = *v.ProviderName
}
if v.ServerSideTokenCheck != nil {
ip["server_side_token_check"] = *v.ServerSideTokenCheck
}
values = append(values, ip)
}
return values
}
func buildLambdaInvokeArn(lambdaArn, region string) string {
apiVersion := "2015-03-31"
return fmt.Sprintf("arn:aws:apigateway:%s:lambda:path/%s/functions/%s/invocations",
region, apiVersion, lambdaArn)
}
func sliceContainsMap(l []interface{}, m map[string]interface{}) (int, bool) {
for i, t := range l {
if reflect.DeepEqual(m, t.(map[string]interface{})) {
return i, true
}
}
return -1, false
}
func expandAwsSsmTargets(d *schema.ResourceData) []*ssm.Target {
var targets []*ssm.Target
targetConfig := d.Get("targets").([]interface{})
for _, tConfig := range targetConfig {
config := tConfig.(map[string]interface{})
target := &ssm.Target{
Key: aws.String(config["key"].(string)),
Values: expandStringList(config["values"].([]interface{})),
}
targets = append(targets, target)
}
return targets
}
func flattenAwsSsmTargets(targets []*ssm.Target) []map[string]interface{} {
if len(targets) == 0 {
return nil
}
result := make([]map[string]interface{}, 0, len(targets))
target := targets[0]
t := make(map[string]interface{})
t["key"] = *target.Key
t["values"] = flattenStringList(target.Values)
result = append(result, t)
return result
}