github.com/wangzhucn/terraform@v0.6.7-0.20151109233120-4eea011b56b3/builtin/providers/aws/structure.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"regexp"
     8  	"sort"
     9  	"strings"
    10  
    11  	"github.com/aws/aws-sdk-go/aws"
    12  	"github.com/aws/aws-sdk-go/service/cloudformation"
    13  	"github.com/aws/aws-sdk-go/service/directoryservice"
    14  	"github.com/aws/aws-sdk-go/service/ec2"
    15  	"github.com/aws/aws-sdk-go/service/ecs"
    16  	"github.com/aws/aws-sdk-go/service/elasticache"
    17  	elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
    18  	"github.com/aws/aws-sdk-go/service/elb"
    19  	"github.com/aws/aws-sdk-go/service/rds"
    20  	"github.com/aws/aws-sdk-go/service/route53"
    21  	"github.com/hashicorp/terraform/helper/schema"
    22  )
    23  
    24  // Takes the result of flatmap.Expand for an array of listeners and
    25  // returns ELB API compatible objects
    26  func expandListeners(configured []interface{}) ([]*elb.Listener, error) {
    27  	listeners := make([]*elb.Listener, 0, len(configured))
    28  
    29  	// Loop over our configured listeners and create
    30  	// an array of aws-sdk-go compatabile objects
    31  	for _, lRaw := range configured {
    32  		data := lRaw.(map[string]interface{})
    33  
    34  		ip := int64(data["instance_port"].(int))
    35  		lp := int64(data["lb_port"].(int))
    36  		l := &elb.Listener{
    37  			InstancePort:     &ip,
    38  			InstanceProtocol: aws.String(data["instance_protocol"].(string)),
    39  			LoadBalancerPort: &lp,
    40  			Protocol:         aws.String(data["lb_protocol"].(string)),
    41  		}
    42  
    43  		if v, ok := data["ssl_certificate_id"]; ok {
    44  			l.SSLCertificateId = aws.String(v.(string))
    45  		}
    46  
    47  		listeners = append(listeners, l)
    48  	}
    49  
    50  	return listeners, nil
    51  }
    52  
    53  // Takes the result of flatmap. Expand for an array of listeners and
    54  // returns ECS Volume compatible objects
    55  func expandEcsVolumes(configured []interface{}) ([]*ecs.Volume, error) {
    56  	volumes := make([]*ecs.Volume, 0, len(configured))
    57  
    58  	// Loop over our configured volumes and create
    59  	// an array of aws-sdk-go compatible objects
    60  	for _, lRaw := range configured {
    61  		data := lRaw.(map[string]interface{})
    62  
    63  		l := &ecs.Volume{
    64  			Name: aws.String(data["name"].(string)),
    65  			Host: &ecs.HostVolumeProperties{
    66  				SourcePath: aws.String(data["host_path"].(string)),
    67  			},
    68  		}
    69  
    70  		volumes = append(volumes, l)
    71  	}
    72  
    73  	return volumes, nil
    74  }
    75  
    76  // Takes JSON in a string. Decodes JSON into
    77  // an array of ecs.ContainerDefinition compatible objects
    78  func expandEcsContainerDefinitions(rawDefinitions string) ([]*ecs.ContainerDefinition, error) {
    79  	var definitions []*ecs.ContainerDefinition
    80  
    81  	err := json.Unmarshal([]byte(rawDefinitions), &definitions)
    82  	if err != nil {
    83  		return nil, fmt.Errorf("Error decoding JSON: %s", err)
    84  	}
    85  
    86  	return definitions, nil
    87  }
    88  
    89  // Takes the result of flatmap. Expand for an array of load balancers and
    90  // returns ecs.LoadBalancer compatible objects
    91  func expandEcsLoadBalancers(configured []interface{}) []*ecs.LoadBalancer {
    92  	loadBalancers := make([]*ecs.LoadBalancer, 0, len(configured))
    93  
    94  	// Loop over our configured load balancers and create
    95  	// an array of aws-sdk-go compatible objects
    96  	for _, lRaw := range configured {
    97  		data := lRaw.(map[string]interface{})
    98  
    99  		l := &ecs.LoadBalancer{
   100  			ContainerName:    aws.String(data["container_name"].(string)),
   101  			ContainerPort:    aws.Int64(int64(data["container_port"].(int))),
   102  			LoadBalancerName: aws.String(data["elb_name"].(string)),
   103  		}
   104  
   105  		loadBalancers = append(loadBalancers, l)
   106  	}
   107  
   108  	return loadBalancers
   109  }
   110  
   111  // Takes the result of flatmap.Expand for an array of ingress/egress security
   112  // group rules and returns EC2 API compatible objects. This function will error
   113  // if it finds invalid permissions input, namely a protocol of "-1" with either
   114  // to_port or from_port set to a non-zero value.
   115  func expandIPPerms(
   116  	group *ec2.SecurityGroup, configured []interface{}) ([]*ec2.IpPermission, error) {
   117  	vpc := group.VpcId != nil
   118  
   119  	perms := make([]*ec2.IpPermission, len(configured))
   120  	for i, mRaw := range configured {
   121  		var perm ec2.IpPermission
   122  		m := mRaw.(map[string]interface{})
   123  
   124  		perm.FromPort = aws.Int64(int64(m["from_port"].(int)))
   125  		perm.ToPort = aws.Int64(int64(m["to_port"].(int)))
   126  		perm.IpProtocol = aws.String(m["protocol"].(string))
   127  
   128  		// When protocol is "-1", AWS won't store any ports for the
   129  		// rule, but also won't error if the user specifies ports other
   130  		// than '0'. Force the user to make a deliberate '0' port
   131  		// choice when specifying a "-1" protocol, and tell them about
   132  		// AWS's behavior in the error message.
   133  		if *perm.IpProtocol == "-1" && (*perm.FromPort != 0 || *perm.ToPort != 0) {
   134  			return nil, fmt.Errorf(
   135  				"from_port (%d) and to_port (%d) must both be 0 to use the the 'ALL' \"-1\" protocol!",
   136  				*perm.FromPort, *perm.ToPort)
   137  		}
   138  
   139  		var groups []string
   140  		if raw, ok := m["security_groups"]; ok {
   141  			list := raw.(*schema.Set).List()
   142  			for _, v := range list {
   143  				groups = append(groups, v.(string))
   144  			}
   145  		}
   146  		if v, ok := m["self"]; ok && v.(bool) {
   147  			if vpc {
   148  				groups = append(groups, *group.GroupId)
   149  			} else {
   150  				groups = append(groups, *group.GroupName)
   151  			}
   152  		}
   153  
   154  		if len(groups) > 0 {
   155  			perm.UserIdGroupPairs = make([]*ec2.UserIdGroupPair, len(groups))
   156  			for i, name := range groups {
   157  				ownerId, id := "", name
   158  				if items := strings.Split(id, "/"); len(items) > 1 {
   159  					ownerId, id = items[0], items[1]
   160  				}
   161  
   162  				perm.UserIdGroupPairs[i] = &ec2.UserIdGroupPair{
   163  					GroupId: aws.String(id),
   164  				}
   165  
   166  				if ownerId != "" {
   167  					perm.UserIdGroupPairs[i].UserId = aws.String(ownerId)
   168  				}
   169  
   170  				if !vpc {
   171  					perm.UserIdGroupPairs[i].GroupId = nil
   172  					perm.UserIdGroupPairs[i].GroupName = aws.String(id)
   173  				}
   174  			}
   175  		}
   176  
   177  		if raw, ok := m["cidr_blocks"]; ok {
   178  			list := raw.([]interface{})
   179  			for _, v := range list {
   180  				perm.IpRanges = append(perm.IpRanges, &ec2.IpRange{CidrIp: aws.String(v.(string))})
   181  			}
   182  		}
   183  
   184  		perms[i] = &perm
   185  	}
   186  
   187  	return perms, nil
   188  }
   189  
   190  // Takes the result of flatmap.Expand for an array of parameters and
   191  // returns Parameter API compatible objects
   192  func expandParameters(configured []interface{}) ([]*rds.Parameter, error) {
   193  	var parameters []*rds.Parameter
   194  
   195  	// Loop over our configured parameters and create
   196  	// an array of aws-sdk-go compatabile objects
   197  	for _, pRaw := range configured {
   198  		data := pRaw.(map[string]interface{})
   199  
   200  		if data["name"].(string) == "" {
   201  			continue
   202  		}
   203  
   204  		p := &rds.Parameter{
   205  			ApplyMethod:    aws.String(data["apply_method"].(string)),
   206  			ParameterName:  aws.String(data["name"].(string)),
   207  			ParameterValue: aws.String(data["value"].(string)),
   208  		}
   209  
   210  		parameters = append(parameters, p)
   211  	}
   212  
   213  	return parameters, nil
   214  }
   215  
   216  // Takes the result of flatmap.Expand for an array of parameters and
   217  // returns Parameter API compatible objects
   218  func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.ParameterNameValue, error) {
   219  	parameters := make([]*elasticache.ParameterNameValue, 0, len(configured))
   220  
   221  	// Loop over our configured parameters and create
   222  	// an array of aws-sdk-go compatabile objects
   223  	for _, pRaw := range configured {
   224  		data := pRaw.(map[string]interface{})
   225  
   226  		p := &elasticache.ParameterNameValue{
   227  			ParameterName:  aws.String(data["name"].(string)),
   228  			ParameterValue: aws.String(data["value"].(string)),
   229  		}
   230  
   231  		parameters = append(parameters, p)
   232  	}
   233  
   234  	return parameters, nil
   235  }
   236  
   237  // Flattens a health check into something that flatmap.Flatten()
   238  // can handle
   239  func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} {
   240  	result := make([]map[string]interface{}, 0, 1)
   241  
   242  	chk := make(map[string]interface{})
   243  	chk["unhealthy_threshold"] = *check.UnhealthyThreshold
   244  	chk["healthy_threshold"] = *check.HealthyThreshold
   245  	chk["target"] = *check.Target
   246  	chk["timeout"] = *check.Timeout
   247  	chk["interval"] = *check.Interval
   248  
   249  	result = append(result, chk)
   250  
   251  	return result
   252  }
   253  
   254  // Flattens an array of UserSecurityGroups into a []string
   255  func flattenSecurityGroups(list []*ec2.UserIdGroupPair) []string {
   256  	result := make([]string, 0, len(list))
   257  	for _, g := range list {
   258  		result = append(result, *g.GroupId)
   259  	}
   260  	return result
   261  }
   262  
   263  // Flattens an array of Instances into a []string
   264  func flattenInstances(list []*elb.Instance) []string {
   265  	result := make([]string, 0, len(list))
   266  	for _, i := range list {
   267  		result = append(result, *i.InstanceId)
   268  	}
   269  	return result
   270  }
   271  
   272  // Expands an array of String Instance IDs into a []Instances
   273  func expandInstanceString(list []interface{}) []*elb.Instance {
   274  	result := make([]*elb.Instance, 0, len(list))
   275  	for _, i := range list {
   276  		result = append(result, &elb.Instance{InstanceId: aws.String(i.(string))})
   277  	}
   278  	return result
   279  }
   280  
   281  // Flattens an array of Backend Descriptions into a a map of instance_port to policy names.
   282  func flattenBackendPolicies(backends []*elb.BackendServerDescription) map[int64][]string {
   283  	policies := make(map[int64][]string)
   284  	for _, i := range backends {
   285  		for _, p := range i.PolicyNames {
   286  			policies[*i.InstancePort] = append(policies[*i.InstancePort], *p)
   287  		}
   288  		sort.Strings(policies[*i.InstancePort])
   289  	}
   290  	return policies
   291  }
   292  
   293  // Flattens an array of Listeners into a []map[string]interface{}
   294  func flattenListeners(list []*elb.ListenerDescription) []map[string]interface{} {
   295  	result := make([]map[string]interface{}, 0, len(list))
   296  	for _, i := range list {
   297  		l := map[string]interface{}{
   298  			"instance_port":     *i.Listener.InstancePort,
   299  			"instance_protocol": strings.ToLower(*i.Listener.InstanceProtocol),
   300  			"lb_port":           *i.Listener.LoadBalancerPort,
   301  			"lb_protocol":       strings.ToLower(*i.Listener.Protocol),
   302  		}
   303  		// SSLCertificateID is optional, and may be nil
   304  		if i.Listener.SSLCertificateId != nil {
   305  			l["ssl_certificate_id"] = *i.Listener.SSLCertificateId
   306  		}
   307  		result = append(result, l)
   308  	}
   309  	return result
   310  }
   311  
   312  // Flattens an array of Volumes into a []map[string]interface{}
   313  func flattenEcsVolumes(list []*ecs.Volume) []map[string]interface{} {
   314  	result := make([]map[string]interface{}, 0, len(list))
   315  	for _, volume := range list {
   316  		l := map[string]interface{}{
   317  			"name":      *volume.Name,
   318  			"host_path": *volume.Host.SourcePath,
   319  		}
   320  		result = append(result, l)
   321  	}
   322  	return result
   323  }
   324  
   325  // Flattens an array of ECS LoadBalancers into a []map[string]interface{}
   326  func flattenEcsLoadBalancers(list []*ecs.LoadBalancer) []map[string]interface{} {
   327  	result := make([]map[string]interface{}, 0, len(list))
   328  	for _, loadBalancer := range list {
   329  		l := map[string]interface{}{
   330  			"elb_name":       *loadBalancer.LoadBalancerName,
   331  			"container_name": *loadBalancer.ContainerName,
   332  			"container_port": *loadBalancer.ContainerPort,
   333  		}
   334  		result = append(result, l)
   335  	}
   336  	return result
   337  }
   338  
   339  // Encodes an array of ecs.ContainerDefinitions into a JSON string
   340  func flattenEcsContainerDefinitions(definitions []*ecs.ContainerDefinition) (string, error) {
   341  	byteArray, err := json.Marshal(definitions)
   342  	if err != nil {
   343  		return "", fmt.Errorf("Error encoding to JSON: %s", err)
   344  	}
   345  
   346  	n := bytes.Index(byteArray, []byte{0})
   347  	return string(byteArray[:n]), nil
   348  }
   349  
   350  // Flattens an array of Parameters into a []map[string]interface{}
   351  func flattenParameters(list []*rds.Parameter) []map[string]interface{} {
   352  	result := make([]map[string]interface{}, 0, len(list))
   353  	for _, i := range list {
   354  		result = append(result, map[string]interface{}{
   355  			"name":  strings.ToLower(*i.ParameterName),
   356  			"value": strings.ToLower(*i.ParameterValue),
   357  		})
   358  	}
   359  	return result
   360  }
   361  
   362  // Flattens an array of Parameters into a []map[string]interface{}
   363  func flattenElastiCacheParameters(list []*elasticache.Parameter) []map[string]interface{} {
   364  	result := make([]map[string]interface{}, 0, len(list))
   365  	for _, i := range list {
   366  		result = append(result, map[string]interface{}{
   367  			"name":  strings.ToLower(*i.ParameterName),
   368  			"value": strings.ToLower(*i.ParameterValue),
   369  		})
   370  	}
   371  	return result
   372  }
   373  
   374  // Takes the result of flatmap.Expand for an array of strings
   375  // and returns a []*string
   376  func expandStringList(configured []interface{}) []*string {
   377  	vs := make([]*string, 0, len(configured))
   378  	for _, v := range configured {
   379  		vs = append(vs, aws.String(v.(string)))
   380  	}
   381  	return vs
   382  }
   383  
   384  // Takes list of pointers to strings. Expand to an array
   385  // of raw strings and returns a []interface{}
   386  // to keep compatibility w/ schema.NewSetschema.NewSet
   387  func flattenStringList(list []*string) []interface{} {
   388  	vs := make([]interface{}, 0, len(list))
   389  	for _, v := range list {
   390  		vs = append(vs, *v)
   391  	}
   392  	return vs
   393  }
   394  
   395  //Flattens an array of private ip addresses into a []string, where the elements returned are the IP strings e.g. "192.168.0.0"
   396  func flattenNetworkInterfacesPrivateIPAddresses(dtos []*ec2.NetworkInterfacePrivateIpAddress) []string {
   397  	ips := make([]string, 0, len(dtos))
   398  	for _, v := range dtos {
   399  		ip := *v.PrivateIpAddress
   400  		ips = append(ips, ip)
   401  	}
   402  	return ips
   403  }
   404  
   405  //Flattens security group identifiers into a []string, where the elements returned are the GroupIDs
   406  func flattenGroupIdentifiers(dtos []*ec2.GroupIdentifier) []string {
   407  	ids := make([]string, 0, len(dtos))
   408  	for _, v := range dtos {
   409  		group_id := *v.GroupId
   410  		ids = append(ids, group_id)
   411  	}
   412  	return ids
   413  }
   414  
   415  //Expands an array of IPs into a ec2 Private IP Address Spec
   416  func expandPrivateIPAddresses(ips []interface{}) []*ec2.PrivateIpAddressSpecification {
   417  	dtos := make([]*ec2.PrivateIpAddressSpecification, 0, len(ips))
   418  	for i, v := range ips {
   419  		new_private_ip := &ec2.PrivateIpAddressSpecification{
   420  			PrivateIpAddress: aws.String(v.(string)),
   421  		}
   422  
   423  		new_private_ip.Primary = aws.Bool(i == 0)
   424  
   425  		dtos = append(dtos, new_private_ip)
   426  	}
   427  	return dtos
   428  }
   429  
   430  //Flattens network interface attachment into a map[string]interface
   431  func flattenAttachment(a *ec2.NetworkInterfaceAttachment) map[string]interface{} {
   432  	att := make(map[string]interface{})
   433  	att["instance"] = *a.InstanceId
   434  	att["device_index"] = *a.DeviceIndex
   435  	att["attachment_id"] = *a.AttachmentId
   436  	return att
   437  }
   438  
   439  func flattenResourceRecords(recs []*route53.ResourceRecord) []string {
   440  	strs := make([]string, 0, len(recs))
   441  	for _, r := range recs {
   442  		if r.Value != nil {
   443  			s := strings.Replace(*r.Value, "\"", "", 2)
   444  			strs = append(strs, s)
   445  		}
   446  	}
   447  	return strs
   448  }
   449  
   450  func expandResourceRecords(recs []interface{}, typeStr string) []*route53.ResourceRecord {
   451  	records := make([]*route53.ResourceRecord, 0, len(recs))
   452  	for _, r := range recs {
   453  		s := r.(string)
   454  		switch typeStr {
   455  		case "TXT":
   456  			str := fmt.Sprintf("\"%s\"", s)
   457  			records = append(records, &route53.ResourceRecord{Value: aws.String(str)})
   458  		default:
   459  			records = append(records, &route53.ResourceRecord{Value: aws.String(s)})
   460  		}
   461  	}
   462  	return records
   463  }
   464  
   465  func validateRdsId(v interface{}, k string) (ws []string, errors []error) {
   466  	value := v.(string)
   467  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
   468  		errors = append(errors, fmt.Errorf(
   469  			"only lowercase alphanumeric characters and hyphens allowed in %q", k))
   470  	}
   471  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
   472  		errors = append(errors, fmt.Errorf(
   473  			"first character of %q must be a letter", k))
   474  	}
   475  	if regexp.MustCompile(`--`).MatchString(value) {
   476  		errors = append(errors, fmt.Errorf(
   477  			"%q cannot contain two consecutive hyphens", k))
   478  	}
   479  	if regexp.MustCompile(`-$`).MatchString(value) {
   480  		errors = append(errors, fmt.Errorf(
   481  			"%q cannot end with a hyphen", k))
   482  	}
   483  	return
   484  }
   485  
   486  func expandESClusterConfig(m map[string]interface{}) *elasticsearch.ElasticsearchClusterConfig {
   487  	config := elasticsearch.ElasticsearchClusterConfig{}
   488  
   489  	if v, ok := m["dedicated_master_enabled"]; ok {
   490  		isEnabled := v.(bool)
   491  		config.DedicatedMasterEnabled = aws.Bool(isEnabled)
   492  
   493  		if isEnabled {
   494  			if v, ok := m["dedicated_master_count"]; ok && v.(int) > 0 {
   495  				config.DedicatedMasterCount = aws.Int64(int64(v.(int)))
   496  			}
   497  			if v, ok := m["dedicated_master_type"]; ok && v.(string) != "" {
   498  				config.DedicatedMasterType = aws.String(v.(string))
   499  			}
   500  		}
   501  	}
   502  
   503  	if v, ok := m["instance_count"]; ok {
   504  		config.InstanceCount = aws.Int64(int64(v.(int)))
   505  	}
   506  	if v, ok := m["instance_type"]; ok {
   507  		config.InstanceType = aws.String(v.(string))
   508  	}
   509  
   510  	if v, ok := m["zone_awareness_enabled"]; ok {
   511  		config.ZoneAwarenessEnabled = aws.Bool(v.(bool))
   512  	}
   513  
   514  	return &config
   515  }
   516  
   517  func flattenESClusterConfig(c *elasticsearch.ElasticsearchClusterConfig) []map[string]interface{} {
   518  	m := map[string]interface{}{}
   519  
   520  	if c.DedicatedMasterCount != nil {
   521  		m["dedicated_master_count"] = *c.DedicatedMasterCount
   522  	}
   523  	if c.DedicatedMasterEnabled != nil {
   524  		m["dedicated_master_enabled"] = *c.DedicatedMasterEnabled
   525  	}
   526  	if c.DedicatedMasterType != nil {
   527  		m["dedicated_master_type"] = *c.DedicatedMasterType
   528  	}
   529  	if c.InstanceCount != nil {
   530  		m["instance_count"] = *c.InstanceCount
   531  	}
   532  	if c.InstanceType != nil {
   533  		m["instance_type"] = *c.InstanceType
   534  	}
   535  	if c.ZoneAwarenessEnabled != nil {
   536  		m["zone_awareness_enabled"] = *c.ZoneAwarenessEnabled
   537  	}
   538  
   539  	return []map[string]interface{}{m}
   540  }
   541  
   542  func flattenESEBSOptions(o *elasticsearch.EBSOptions) []map[string]interface{} {
   543  	m := map[string]interface{}{}
   544  
   545  	if o.EBSEnabled != nil {
   546  		m["ebs_enabled"] = *o.EBSEnabled
   547  	}
   548  	if o.Iops != nil {
   549  		m["iops"] = *o.Iops
   550  	}
   551  	if o.VolumeSize != nil {
   552  		m["volume_size"] = *o.VolumeSize
   553  	}
   554  	if o.VolumeType != nil {
   555  		m["volume_type"] = *o.VolumeType
   556  	}
   557  
   558  	return []map[string]interface{}{m}
   559  }
   560  
   561  func expandESEBSOptions(m map[string]interface{}) *elasticsearch.EBSOptions {
   562  	options := elasticsearch.EBSOptions{}
   563  
   564  	if v, ok := m["ebs_enabled"]; ok {
   565  		options.EBSEnabled = aws.Bool(v.(bool))
   566  	}
   567  	if v, ok := m["iops"]; ok && v.(int) > 0 {
   568  		options.Iops = aws.Int64(int64(v.(int)))
   569  	}
   570  	if v, ok := m["volume_size"]; ok && v.(int) > 0 {
   571  		options.VolumeSize = aws.Int64(int64(v.(int)))
   572  	}
   573  	if v, ok := m["volume_type"]; ok && v.(string) != "" {
   574  		options.VolumeType = aws.String(v.(string))
   575  	}
   576  
   577  	return &options
   578  }
   579  
   580  func pointersMapToStringList(pointers map[string]*string) map[string]interface{} {
   581  	list := make(map[string]interface{}, len(pointers))
   582  	for i, v := range pointers {
   583  		list[i] = *v
   584  	}
   585  	return list
   586  }
   587  
   588  func stringMapToPointers(m map[string]interface{}) map[string]*string {
   589  	list := make(map[string]*string, len(m))
   590  	for i, v := range m {
   591  		list[i] = aws.String(v.(string))
   592  	}
   593  	return list
   594  }
   595  
   596  func flattenDSVpcSettings(
   597  	s *directoryservice.DirectoryVpcSettingsDescription) []map[string]interface{} {
   598  	settings := make(map[string]interface{}, 0)
   599  
   600  	settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds))
   601  	settings["vpc_id"] = *s.VpcId
   602  
   603  	return []map[string]interface{}{settings}
   604  }
   605  
   606  func expandCloudFormationParameters(params map[string]interface{}) []*cloudformation.Parameter {
   607  	var cfParams []*cloudformation.Parameter
   608  	for k, v := range params {
   609  		cfParams = append(cfParams, &cloudformation.Parameter{
   610  			ParameterKey:   aws.String(k),
   611  			ParameterValue: aws.String(v.(string)),
   612  		})
   613  	}
   614  
   615  	return cfParams
   616  }
   617  
   618  // flattenCloudFormationParameters is flattening list of
   619  // *cloudformation.Parameters and only returning existing
   620  // parameters to avoid clash with default values
   621  func flattenCloudFormationParameters(cfParams []*cloudformation.Parameter,
   622  	originalParams map[string]interface{}) map[string]interface{} {
   623  	params := make(map[string]interface{}, len(cfParams))
   624  	for _, p := range cfParams {
   625  		_, isConfigured := originalParams[*p.ParameterKey]
   626  		if isConfigured {
   627  			params[*p.ParameterKey] = *p.ParameterValue
   628  		}
   629  	}
   630  	return params
   631  }
   632  
   633  func expandCloudFormationTags(tags map[string]interface{}) []*cloudformation.Tag {
   634  	var cfTags []*cloudformation.Tag
   635  	for k, v := range tags {
   636  		cfTags = append(cfTags, &cloudformation.Tag{
   637  			Key:   aws.String(k),
   638  			Value: aws.String(v.(string)),
   639  		})
   640  	}
   641  	return cfTags
   642  }
   643  
   644  func flattenCloudFormationTags(cfTags []*cloudformation.Tag) map[string]string {
   645  	tags := make(map[string]string, len(cfTags))
   646  	for _, t := range cfTags {
   647  		tags[*t.Key] = *t.Value
   648  	}
   649  	return tags
   650  }
   651  
   652  func flattenCloudFormationOutputs(cfOutputs []*cloudformation.Output) map[string]string {
   653  	outputs := make(map[string]string, len(cfOutputs))
   654  	for _, o := range cfOutputs {
   655  		outputs[*o.OutputKey] = *o.OutputValue
   656  	}
   657  	return outputs
   658  }