github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_codedeploy_deployment_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"regexp"
     8  	"sort"
     9  	"time"
    10  
    11  	"github.com/hashicorp/terraform/helper/hashcode"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  
    15  	"github.com/aws/aws-sdk-go/aws"
    16  	"github.com/aws/aws-sdk-go/aws/awserr"
    17  	"github.com/aws/aws-sdk-go/service/codedeploy"
    18  )
    19  
    20  func resourceAwsCodeDeployDeploymentGroup() *schema.Resource {
    21  	return &schema.Resource{
    22  		Create: resourceAwsCodeDeployDeploymentGroupCreate,
    23  		Read:   resourceAwsCodeDeployDeploymentGroupRead,
    24  		Update: resourceAwsCodeDeployDeploymentGroupUpdate,
    25  		Delete: resourceAwsCodeDeployDeploymentGroupDelete,
    26  
    27  		Schema: map[string]*schema.Schema{
    28  			"app_name": &schema.Schema{
    29  				Type:     schema.TypeString,
    30  				Required: true,
    31  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    32  					value := v.(string)
    33  					if len(value) > 100 {
    34  						errors = append(errors, fmt.Errorf(
    35  							"%q cannot exceed 100 characters", k))
    36  					}
    37  					return
    38  				},
    39  			},
    40  
    41  			"deployment_group_name": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Required: true,
    44  				ForceNew: true,
    45  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    46  					value := v.(string)
    47  					if len(value) > 100 {
    48  						errors = append(errors, fmt.Errorf(
    49  							"%q cannot exceed 100 characters", k))
    50  					}
    51  					return
    52  				},
    53  			},
    54  
    55  			"service_role_arn": &schema.Schema{
    56  				Type:     schema.TypeString,
    57  				Required: true,
    58  			},
    59  
    60  			"alarm_configuration": &schema.Schema{
    61  				Type:     schema.TypeList,
    62  				Optional: true,
    63  				MaxItems: 1,
    64  				Elem: &schema.Resource{
    65  					Schema: map[string]*schema.Schema{
    66  						"alarms": &schema.Schema{
    67  							Type:     schema.TypeSet,
    68  							MaxItems: 10,
    69  							Optional: true,
    70  							Set:      schema.HashString,
    71  							Elem:     &schema.Schema{Type: schema.TypeString},
    72  						},
    73  
    74  						"enabled": &schema.Schema{
    75  							Type:     schema.TypeBool,
    76  							Optional: true,
    77  						},
    78  
    79  						"ignore_poll_alarm_failure": &schema.Schema{
    80  							Type:     schema.TypeBool,
    81  							Optional: true,
    82  							Default:  false,
    83  						},
    84  					},
    85  				},
    86  			},
    87  
    88  			"auto_rollback_configuration": &schema.Schema{
    89  				Type:     schema.TypeList,
    90  				Optional: true,
    91  				MaxItems: 1,
    92  				Elem: &schema.Resource{
    93  					Schema: map[string]*schema.Schema{
    94  						"enabled": &schema.Schema{
    95  							Type:     schema.TypeBool,
    96  							Optional: true,
    97  						},
    98  
    99  						"events": &schema.Schema{
   100  							Type:     schema.TypeSet,
   101  							Optional: true,
   102  							Set:      schema.HashString,
   103  							Elem:     &schema.Schema{Type: schema.TypeString},
   104  						},
   105  					},
   106  				},
   107  			},
   108  
   109  			"autoscaling_groups": &schema.Schema{
   110  				Type:     schema.TypeSet,
   111  				Optional: true,
   112  				Elem:     &schema.Schema{Type: schema.TypeString},
   113  				Set:      schema.HashString,
   114  			},
   115  
   116  			"deployment_config_name": &schema.Schema{
   117  				Type:     schema.TypeString,
   118  				Optional: true,
   119  				Default:  "CodeDeployDefault.OneAtATime",
   120  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   121  					value := v.(string)
   122  					if len(value) > 100 {
   123  						errors = append(errors, fmt.Errorf(
   124  							"%q cannot exceed 100 characters", k))
   125  					}
   126  					return
   127  				},
   128  			},
   129  
   130  			"ec2_tag_filter": &schema.Schema{
   131  				Type:     schema.TypeSet,
   132  				Optional: true,
   133  				Elem: &schema.Resource{
   134  					Schema: map[string]*schema.Schema{
   135  						"key": &schema.Schema{
   136  							Type:     schema.TypeString,
   137  							Optional: true,
   138  						},
   139  
   140  						"type": &schema.Schema{
   141  							Type:         schema.TypeString,
   142  							Optional:     true,
   143  							ValidateFunc: validateTagFilters,
   144  						},
   145  
   146  						"value": &schema.Schema{
   147  							Type:     schema.TypeString,
   148  							Optional: true,
   149  						},
   150  					},
   151  				},
   152  				Set: resourceAwsCodeDeployTagFilterHash,
   153  			},
   154  
   155  			"on_premises_instance_tag_filter": &schema.Schema{
   156  				Type:     schema.TypeSet,
   157  				Optional: true,
   158  				Elem: &schema.Resource{
   159  					Schema: map[string]*schema.Schema{
   160  						"key": &schema.Schema{
   161  							Type:     schema.TypeString,
   162  							Optional: true,
   163  						},
   164  
   165  						"type": &schema.Schema{
   166  							Type:         schema.TypeString,
   167  							Optional:     true,
   168  							ValidateFunc: validateTagFilters,
   169  						},
   170  
   171  						"value": &schema.Schema{
   172  							Type:     schema.TypeString,
   173  							Optional: true,
   174  						},
   175  					},
   176  				},
   177  				Set: resourceAwsCodeDeployTagFilterHash,
   178  			},
   179  
   180  			"trigger_configuration": &schema.Schema{
   181  				Type:     schema.TypeSet,
   182  				Optional: true,
   183  				Elem: &schema.Resource{
   184  					Schema: map[string]*schema.Schema{
   185  						"trigger_events": &schema.Schema{
   186  							Type:     schema.TypeSet,
   187  							Required: true,
   188  							Set:      schema.HashString,
   189  							Elem: &schema.Schema{
   190  								Type:         schema.TypeString,
   191  								ValidateFunc: validateTriggerEvent,
   192  							},
   193  						},
   194  
   195  						"trigger_name": &schema.Schema{
   196  							Type:     schema.TypeString,
   197  							Required: true,
   198  						},
   199  
   200  						"trigger_target_arn": &schema.Schema{
   201  							Type:     schema.TypeString,
   202  							Required: true,
   203  						},
   204  					},
   205  				},
   206  				Set: resourceAwsCodeDeployTriggerConfigHash,
   207  			},
   208  		},
   209  	}
   210  }
   211  
   212  func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta interface{}) error {
   213  	conn := meta.(*AWSClient).codedeployconn
   214  
   215  	application := d.Get("app_name").(string)
   216  	deploymentGroup := d.Get("deployment_group_name").(string)
   217  
   218  	input := codedeploy.CreateDeploymentGroupInput{
   219  		ApplicationName:     aws.String(application),
   220  		DeploymentGroupName: aws.String(deploymentGroup),
   221  		ServiceRoleArn:      aws.String(d.Get("service_role_arn").(string)),
   222  	}
   223  	if attr, ok := d.GetOk("deployment_config_name"); ok {
   224  		input.DeploymentConfigName = aws.String(attr.(string))
   225  	}
   226  	if attr, ok := d.GetOk("autoscaling_groups"); ok {
   227  		input.AutoScalingGroups = expandStringList(attr.(*schema.Set).List())
   228  	}
   229  	if attr, ok := d.GetOk("on_premises_instance_tag_filter"); ok {
   230  		onPremFilters := buildOnPremTagFilters(attr.(*schema.Set).List())
   231  		input.OnPremisesInstanceTagFilters = onPremFilters
   232  	}
   233  	if attr, ok := d.GetOk("ec2_tag_filter"); ok {
   234  		ec2TagFilters := buildEC2TagFilters(attr.(*schema.Set).List())
   235  		input.Ec2TagFilters = ec2TagFilters
   236  	}
   237  	if attr, ok := d.GetOk("trigger_configuration"); ok {
   238  		triggerConfigs := buildTriggerConfigs(attr.(*schema.Set).List())
   239  		input.TriggerConfigurations = triggerConfigs
   240  	}
   241  
   242  	if attr, ok := d.GetOk("auto_rollback_configuration"); ok {
   243  		input.AutoRollbackConfiguration = buildAutoRollbackConfig(attr.([]interface{}))
   244  	}
   245  
   246  	if attr, ok := d.GetOk("alarm_configuration"); ok {
   247  		input.AlarmConfiguration = buildAlarmConfig(attr.([]interface{}))
   248  	}
   249  
   250  	// Retry to handle IAM role eventual consistency.
   251  	var resp *codedeploy.CreateDeploymentGroupOutput
   252  	var err error
   253  	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
   254  		resp, err = conn.CreateDeploymentGroup(&input)
   255  		if err != nil {
   256  			retry := false
   257  			codedeployErr, ok := err.(awserr.Error)
   258  			if !ok {
   259  				return resource.NonRetryableError(err)
   260  			}
   261  			if codedeployErr.Code() == "InvalidRoleException" {
   262  				retry = true
   263  			}
   264  			if codedeployErr.Code() == "InvalidTriggerConfigException" {
   265  				r := regexp.MustCompile("^Topic ARN .+ is not valid$")
   266  				if r.MatchString(codedeployErr.Message()) {
   267  					retry = true
   268  				}
   269  			}
   270  			if retry {
   271  				log.Printf("[DEBUG] Trying to create deployment group again: %q",
   272  					codedeployErr.Message())
   273  				return resource.RetryableError(err)
   274  			}
   275  
   276  			return resource.NonRetryableError(err)
   277  		}
   278  		return nil
   279  	})
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	d.SetId(*resp.DeploymentGroupId)
   285  
   286  	return resourceAwsCodeDeployDeploymentGroupRead(d, meta)
   287  }
   288  
   289  func resourceAwsCodeDeployDeploymentGroupRead(d *schema.ResourceData, meta interface{}) error {
   290  	conn := meta.(*AWSClient).codedeployconn
   291  
   292  	log.Printf("[DEBUG] Reading CodeDeploy DeploymentGroup %s", d.Id())
   293  	resp, err := conn.GetDeploymentGroup(&codedeploy.GetDeploymentGroupInput{
   294  		ApplicationName:     aws.String(d.Get("app_name").(string)),
   295  		DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)),
   296  	})
   297  	if err != nil {
   298  		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "DeploymentGroupDoesNotExistException" {
   299  			log.Printf("[INFO] CodeDeployment DeploymentGroup %s not found", d.Get("deployment_group_name").(string))
   300  			d.SetId("")
   301  			return nil
   302  		}
   303  
   304  		return err
   305  	}
   306  
   307  	d.Set("app_name", resp.DeploymentGroupInfo.ApplicationName)
   308  	d.Set("autoscaling_groups", resp.DeploymentGroupInfo.AutoScalingGroups)
   309  	d.Set("deployment_config_name", resp.DeploymentGroupInfo.DeploymentConfigName)
   310  	d.Set("deployment_group_name", resp.DeploymentGroupInfo.DeploymentGroupName)
   311  	d.Set("service_role_arn", resp.DeploymentGroupInfo.ServiceRoleArn)
   312  	if err := d.Set("ec2_tag_filter", ec2TagFiltersToMap(resp.DeploymentGroupInfo.Ec2TagFilters)); err != nil {
   313  		return err
   314  	}
   315  	if err := d.Set("on_premises_instance_tag_filter", onPremisesTagFiltersToMap(resp.DeploymentGroupInfo.OnPremisesInstanceTagFilters)); err != nil {
   316  		return err
   317  	}
   318  	if err := d.Set("trigger_configuration", triggerConfigsToMap(resp.DeploymentGroupInfo.TriggerConfigurations)); err != nil {
   319  		return err
   320  	}
   321  
   322  	if err := d.Set("auto_rollback_configuration", autoRollbackConfigToMap(resp.DeploymentGroupInfo.AutoRollbackConfiguration)); err != nil {
   323  		return err
   324  	}
   325  
   326  	if err := d.Set("alarm_configuration", alarmConfigToMap(resp.DeploymentGroupInfo.AlarmConfiguration)); err != nil {
   327  		return err
   328  	}
   329  
   330  	return nil
   331  }
   332  
   333  func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   334  	conn := meta.(*AWSClient).codedeployconn
   335  
   336  	input := codedeploy.UpdateDeploymentGroupInput{
   337  		ApplicationName:            aws.String(d.Get("app_name").(string)),
   338  		CurrentDeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)),
   339  		ServiceRoleArn:             aws.String(d.Get("service_role_arn").(string)),
   340  	}
   341  
   342  	if d.HasChange("autoscaling_groups") {
   343  		_, n := d.GetChange("autoscaling_groups")
   344  		input.AutoScalingGroups = expandStringList(n.(*schema.Set).List())
   345  	}
   346  	if d.HasChange("deployment_config_name") {
   347  		_, n := d.GetChange("deployment_config_name")
   348  		input.DeploymentConfigName = aws.String(n.(string))
   349  	}
   350  	if d.HasChange("deployment_group_name") {
   351  		_, n := d.GetChange("deployment_group_name")
   352  		input.NewDeploymentGroupName = aws.String(n.(string))
   353  	}
   354  
   355  	// TagFilters aren't like tags. They don't append. They simply replace.
   356  	if d.HasChange("on_premises_instance_tag_filter") {
   357  		_, n := d.GetChange("on_premises_instance_tag_filter")
   358  		onPremFilters := buildOnPremTagFilters(n.(*schema.Set).List())
   359  		input.OnPremisesInstanceTagFilters = onPremFilters
   360  	}
   361  	if d.HasChange("ec2_tag_filter") {
   362  		_, n := d.GetChange("ec2_tag_filter")
   363  		ec2Filters := buildEC2TagFilters(n.(*schema.Set).List())
   364  		input.Ec2TagFilters = ec2Filters
   365  	}
   366  	if d.HasChange("trigger_configuration") {
   367  		_, n := d.GetChange("trigger_configuration")
   368  		triggerConfigs := buildTriggerConfigs(n.(*schema.Set).List())
   369  		input.TriggerConfigurations = triggerConfigs
   370  	}
   371  
   372  	if d.HasChange("auto_rollback_configuration") {
   373  		_, n := d.GetChange("auto_rollback_configuration")
   374  		input.AutoRollbackConfiguration = buildAutoRollbackConfig(n.([]interface{}))
   375  	}
   376  
   377  	if d.HasChange("alarm_configuration") {
   378  		_, n := d.GetChange("alarm_configuration")
   379  		input.AlarmConfiguration = buildAlarmConfig(n.([]interface{}))
   380  	}
   381  
   382  	log.Printf("[DEBUG] Updating CodeDeploy DeploymentGroup %s", d.Id())
   383  	// Retry to handle IAM role eventual consistency.
   384  	err := resource.Retry(5*time.Minute, func() *resource.RetryError {
   385  		_, err := conn.UpdateDeploymentGroup(&input)
   386  		if err != nil {
   387  			retry := false
   388  			codedeployErr, ok := err.(awserr.Error)
   389  			if !ok {
   390  				return resource.NonRetryableError(err)
   391  			}
   392  			if codedeployErr.Code() == "InvalidRoleException" {
   393  				retry = true
   394  			}
   395  			if codedeployErr.Code() == "InvalidTriggerConfigException" {
   396  				r := regexp.MustCompile("^Topic ARN .+ is not valid$")
   397  				if r.MatchString(codedeployErr.Message()) {
   398  					retry = true
   399  				}
   400  			}
   401  			if retry {
   402  				log.Printf("[DEBUG] Retrying Code Deployment Group Update: %q",
   403  					codedeployErr.Message())
   404  				return resource.RetryableError(err)
   405  			}
   406  
   407  			return resource.NonRetryableError(err)
   408  		}
   409  		return nil
   410  	})
   411  
   412  	if err != nil {
   413  		return err
   414  	}
   415  
   416  	return resourceAwsCodeDeployDeploymentGroupRead(d, meta)
   417  }
   418  
   419  func resourceAwsCodeDeployDeploymentGroupDelete(d *schema.ResourceData, meta interface{}) error {
   420  	conn := meta.(*AWSClient).codedeployconn
   421  
   422  	log.Printf("[DEBUG] Deleting CodeDeploy DeploymentGroup %s", d.Id())
   423  	_, err := conn.DeleteDeploymentGroup(&codedeploy.DeleteDeploymentGroupInput{
   424  		ApplicationName:     aws.String(d.Get("app_name").(string)),
   425  		DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)),
   426  	})
   427  	if err != nil {
   428  		return err
   429  	}
   430  
   431  	d.SetId("")
   432  
   433  	return nil
   434  }
   435  
   436  // buildOnPremTagFilters converts raw schema lists into a list of
   437  // codedeploy.TagFilters.
   438  func buildOnPremTagFilters(configured []interface{}) []*codedeploy.TagFilter {
   439  	filters := make([]*codedeploy.TagFilter, 0)
   440  	for _, raw := range configured {
   441  		var filter codedeploy.TagFilter
   442  		m := raw.(map[string]interface{})
   443  
   444  		if v, ok := m["key"]; ok {
   445  			filter.Key = aws.String(v.(string))
   446  		}
   447  		if v, ok := m["type"]; ok {
   448  			filter.Type = aws.String(v.(string))
   449  		}
   450  		if v, ok := m["value"]; ok {
   451  			filter.Value = aws.String(v.(string))
   452  		}
   453  
   454  		filters = append(filters, &filter)
   455  	}
   456  
   457  	return filters
   458  }
   459  
   460  // buildEC2TagFilters converts raw schema lists into a list of
   461  // codedeploy.EC2TagFilters.
   462  func buildEC2TagFilters(configured []interface{}) []*codedeploy.EC2TagFilter {
   463  	filters := make([]*codedeploy.EC2TagFilter, 0)
   464  	for _, raw := range configured {
   465  		var filter codedeploy.EC2TagFilter
   466  		m := raw.(map[string]interface{})
   467  
   468  		filter.Key = aws.String(m["key"].(string))
   469  		filter.Type = aws.String(m["type"].(string))
   470  		filter.Value = aws.String(m["value"].(string))
   471  
   472  		filters = append(filters, &filter)
   473  	}
   474  
   475  	return filters
   476  }
   477  
   478  // buildTriggerConfigs converts a raw schema list into a list of
   479  // codedeploy.TriggerConfig.
   480  func buildTriggerConfigs(configured []interface{}) []*codedeploy.TriggerConfig {
   481  	configs := make([]*codedeploy.TriggerConfig, 0, len(configured))
   482  	for _, raw := range configured {
   483  		var config codedeploy.TriggerConfig
   484  		m := raw.(map[string]interface{})
   485  
   486  		config.TriggerEvents = expandStringSet(m["trigger_events"].(*schema.Set))
   487  		config.TriggerName = aws.String(m["trigger_name"].(string))
   488  		config.TriggerTargetArn = aws.String(m["trigger_target_arn"].(string))
   489  
   490  		configs = append(configs, &config)
   491  	}
   492  	return configs
   493  }
   494  
   495  // buildAutoRollbackConfig converts a raw schema list containing a map[string]interface{}
   496  // into a single codedeploy.AutoRollbackConfiguration
   497  func buildAutoRollbackConfig(configured []interface{}) *codedeploy.AutoRollbackConfiguration {
   498  	result := &codedeploy.AutoRollbackConfiguration{}
   499  
   500  	if len(configured) == 1 {
   501  		config := configured[0].(map[string]interface{})
   502  		result.Enabled = aws.Bool(config["enabled"].(bool))
   503  		result.Events = expandStringSet(config["events"].(*schema.Set))
   504  	} else { // delete the configuration
   505  		result.Enabled = aws.Bool(false)
   506  		result.Events = make([]*string, 0)
   507  	}
   508  
   509  	return result
   510  }
   511  
   512  // buildAlarmConfig converts a raw schema list containing a map[string]interface{}
   513  // into a single codedeploy.AlarmConfiguration
   514  func buildAlarmConfig(configured []interface{}) *codedeploy.AlarmConfiguration {
   515  	result := &codedeploy.AlarmConfiguration{}
   516  
   517  	if len(configured) == 1 {
   518  		config := configured[0].(map[string]interface{})
   519  		names := expandStringSet(config["alarms"].(*schema.Set))
   520  		alarms := make([]*codedeploy.Alarm, 0, len(names))
   521  
   522  		for _, name := range names {
   523  			alarm := &codedeploy.Alarm{
   524  				Name: name,
   525  			}
   526  			alarms = append(alarms, alarm)
   527  		}
   528  
   529  		result.Alarms = alarms
   530  		result.Enabled = aws.Bool(config["enabled"].(bool))
   531  		result.IgnorePollAlarmFailure = aws.Bool(config["ignore_poll_alarm_failure"].(bool))
   532  	} else { // delete the configuration
   533  		result.Alarms = make([]*codedeploy.Alarm, 0)
   534  		result.Enabled = aws.Bool(false)
   535  		result.IgnorePollAlarmFailure = aws.Bool(false)
   536  	}
   537  
   538  	return result
   539  }
   540  
   541  // ec2TagFiltersToMap converts lists of tag filters into a []map[string]string.
   542  func ec2TagFiltersToMap(list []*codedeploy.EC2TagFilter) []map[string]string {
   543  	result := make([]map[string]string, 0, len(list))
   544  	for _, tf := range list {
   545  		l := make(map[string]string)
   546  		if tf.Key != nil && *tf.Key != "" {
   547  			l["key"] = *tf.Key
   548  		}
   549  		if tf.Value != nil && *tf.Value != "" {
   550  			l["value"] = *tf.Value
   551  		}
   552  		if tf.Type != nil && *tf.Type != "" {
   553  			l["type"] = *tf.Type
   554  		}
   555  		result = append(result, l)
   556  	}
   557  	return result
   558  }
   559  
   560  // onPremisesTagFiltersToMap converts lists of on-prem tag filters into a []map[string]string.
   561  func onPremisesTagFiltersToMap(list []*codedeploy.TagFilter) []map[string]string {
   562  	result := make([]map[string]string, 0, len(list))
   563  	for _, tf := range list {
   564  		l := make(map[string]string)
   565  		if tf.Key != nil && *tf.Key != "" {
   566  			l["key"] = *tf.Key
   567  		}
   568  		if tf.Value != nil && *tf.Value != "" {
   569  			l["value"] = *tf.Value
   570  		}
   571  		if tf.Type != nil && *tf.Type != "" {
   572  			l["type"] = *tf.Type
   573  		}
   574  		result = append(result, l)
   575  	}
   576  	return result
   577  }
   578  
   579  // triggerConfigsToMap converts a list of []*codedeploy.TriggerConfig into a []map[string]interface{}
   580  func triggerConfigsToMap(list []*codedeploy.TriggerConfig) []map[string]interface{} {
   581  	result := make([]map[string]interface{}, 0, len(list))
   582  	for _, tc := range list {
   583  		item := make(map[string]interface{})
   584  		item["trigger_events"] = schema.NewSet(schema.HashString, flattenStringList(tc.TriggerEvents))
   585  		item["trigger_name"] = *tc.TriggerName
   586  		item["trigger_target_arn"] = *tc.TriggerTargetArn
   587  		result = append(result, item)
   588  	}
   589  	return result
   590  }
   591  
   592  // autoRollbackConfigToMap converts a codedeploy.AutoRollbackConfiguration
   593  // into a []map[string]interface{} list containing a single item
   594  func autoRollbackConfigToMap(config *codedeploy.AutoRollbackConfiguration) []map[string]interface{} {
   595  	result := make([]map[string]interface{}, 0, 1)
   596  
   597  	// only create configurations that are enabled or temporarily disabled (retaining events)
   598  	// otherwise empty configurations will be created
   599  	if config != nil && (*config.Enabled == true || len(config.Events) > 0) {
   600  		item := make(map[string]interface{})
   601  		item["enabled"] = *config.Enabled
   602  		item["events"] = schema.NewSet(schema.HashString, flattenStringList(config.Events))
   603  		result = append(result, item)
   604  	}
   605  
   606  	return result
   607  }
   608  
   609  // alarmConfigToMap converts a codedeploy.AlarmConfiguration
   610  // into a []map[string]interface{} list containing a single item
   611  func alarmConfigToMap(config *codedeploy.AlarmConfiguration) []map[string]interface{} {
   612  	result := make([]map[string]interface{}, 0, 1)
   613  
   614  	// only create configurations that are enabled or temporarily disabled (retaining alarms)
   615  	// otherwise empty configurations will be created
   616  	if config != nil && (*config.Enabled == true || len(config.Alarms) > 0) {
   617  		names := make([]*string, 0, len(config.Alarms))
   618  		for _, alarm := range config.Alarms {
   619  			names = append(names, alarm.Name)
   620  		}
   621  
   622  		item := make(map[string]interface{})
   623  		item["alarms"] = schema.NewSet(schema.HashString, flattenStringList(names))
   624  		item["enabled"] = *config.Enabled
   625  		item["ignore_poll_alarm_failure"] = *config.IgnorePollAlarmFailure
   626  
   627  		result = append(result, item)
   628  	}
   629  
   630  	return result
   631  }
   632  
   633  func resourceAwsCodeDeployTagFilterHash(v interface{}) int {
   634  	var buf bytes.Buffer
   635  	m := v.(map[string]interface{})
   636  
   637  	// Nothing's actually required in tag filters, so we must check the
   638  	// presence of all values before attempting a hash.
   639  	if v, ok := m["key"]; ok {
   640  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   641  	}
   642  	if v, ok := m["type"]; ok {
   643  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   644  	}
   645  	if v, ok := m["value"]; ok {
   646  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   647  	}
   648  
   649  	return hashcode.String(buf.String())
   650  }
   651  
   652  func resourceAwsCodeDeployTriggerConfigHash(v interface{}) int {
   653  	var buf bytes.Buffer
   654  	m := v.(map[string]interface{})
   655  	buf.WriteString(fmt.Sprintf("%s-", m["trigger_name"].(string)))
   656  	buf.WriteString(fmt.Sprintf("%s-", m["trigger_target_arn"].(string)))
   657  
   658  	if triggerEvents, ok := m["trigger_events"]; ok {
   659  		names := triggerEvents.(*schema.Set).List()
   660  		strings := make([]string, len(names))
   661  		for i, raw := range names {
   662  			strings[i] = raw.(string)
   663  		}
   664  		sort.Strings(strings)
   665  
   666  		for _, s := range strings {
   667  			buf.WriteString(fmt.Sprintf("%s-", s))
   668  		}
   669  	}
   670  	return hashcode.String(buf.String())
   671  }
   672  
   673  func validateTriggerEvent(v interface{}, k string) (ws []string, errors []error) {
   674  	value := v.(string)
   675  	triggerEvents := map[string]bool{
   676  		"DeploymentStart":    true,
   677  		"DeploymentStop":     true,
   678  		"DeploymentSuccess":  true,
   679  		"DeploymentFailure":  true,
   680  		"DeploymentRollback": true,
   681  		"InstanceStart":      true,
   682  		"InstanceSuccess":    true,
   683  		"InstanceFailure":    true,
   684  	}
   685  
   686  	if !triggerEvents[value] {
   687  		errors = append(errors, fmt.Errorf("%q must be a valid event type value: %q", k, value))
   688  	}
   689  	return
   690  }