github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/aws/resource_aws_elastic_beanstalk_environment.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"regexp"
     7  	"sort"
     8  	"strings"
     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/service/ec2"
    17  	"github.com/aws/aws-sdk-go/service/elasticbeanstalk"
    18  )
    19  
    20  func resourceAwsElasticBeanstalkOptionSetting() *schema.Resource {
    21  	return &schema.Resource{
    22  		Schema: map[string]*schema.Schema{
    23  			"namespace": &schema.Schema{
    24  				Type:     schema.TypeString,
    25  				Required: true,
    26  			},
    27  			"name": &schema.Schema{
    28  				Type:     schema.TypeString,
    29  				Required: true,
    30  			},
    31  			"value": &schema.Schema{
    32  				Type:     schema.TypeString,
    33  				Required: true,
    34  			},
    35  		},
    36  	}
    37  }
    38  
    39  func resourceAwsElasticBeanstalkEnvironment() *schema.Resource {
    40  	return &schema.Resource{
    41  		Create: resourceAwsElasticBeanstalkEnvironmentCreate,
    42  		Read:   resourceAwsElasticBeanstalkEnvironmentRead,
    43  		Update: resourceAwsElasticBeanstalkEnvironmentUpdate,
    44  		Delete: resourceAwsElasticBeanstalkEnvironmentDelete,
    45  
    46  		Schema: map[string]*schema.Schema{
    47  			"name": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Required: true,
    50  				ForceNew: true,
    51  			},
    52  			"application": &schema.Schema{
    53  				Type:     schema.TypeString,
    54  				Required: true,
    55  			},
    56  			"description": &schema.Schema{
    57  				Type:     schema.TypeString,
    58  				Optional: true,
    59  			},
    60  			"cname": &schema.Schema{
    61  				Type:     schema.TypeString,
    62  				Computed: true,
    63  			},
    64  			"cname_prefix": &schema.Schema{
    65  				Type:     schema.TypeString,
    66  				Computed: true,
    67  				Optional: true,
    68  				ForceNew: true,
    69  			},
    70  			"tier": &schema.Schema{
    71  				Type:     schema.TypeString,
    72  				Optional: true,
    73  				Default:  "WebServer",
    74  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    75  					value := v.(string)
    76  					switch value {
    77  					case
    78  						"Worker",
    79  						"WebServer":
    80  						return
    81  					}
    82  					errors = append(errors, fmt.Errorf("%s is not a valid tier. Valid options are WebServer or Worker", value))
    83  					return
    84  				},
    85  				ForceNew: true,
    86  			},
    87  			"setting": &schema.Schema{
    88  				Type:     schema.TypeSet,
    89  				Optional: true,
    90  				Computed: true,
    91  				Elem:     resourceAwsElasticBeanstalkOptionSetting(),
    92  				Set:      optionSettingValueHash,
    93  			},
    94  			"all_settings": &schema.Schema{
    95  				Type:     schema.TypeSet,
    96  				Computed: true,
    97  				Elem:     resourceAwsElasticBeanstalkOptionSetting(),
    98  				Set:      optionSettingValueHash,
    99  			},
   100  			"solution_stack_name": &schema.Schema{
   101  				Type:     schema.TypeString,
   102  				Optional: true,
   103  			},
   104  			"template_name": &schema.Schema{
   105  				Type:          schema.TypeString,
   106  				Optional:      true,
   107  				ConflictsWith: []string{"solution_stack_name"},
   108  			},
   109  			"wait_for_ready_timeout": &schema.Schema{
   110  				Type:     schema.TypeString,
   111  				Optional: true,
   112  				Default:  "10m",
   113  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   114  					value := v.(string)
   115  					duration, err := time.ParseDuration(value)
   116  					if err != nil {
   117  						errors = append(errors, fmt.Errorf(
   118  							"%q cannot be parsed as a duration: %s", k, err))
   119  					}
   120  					if duration < 0 {
   121  						errors = append(errors, fmt.Errorf(
   122  							"%q must be greater than zero", k))
   123  					}
   124  					return
   125  				},
   126  			},
   127  			"autoscaling_groups": &schema.Schema{
   128  				Type:     schema.TypeList,
   129  				Computed: true,
   130  				Elem:     &schema.Schema{Type: schema.TypeString},
   131  			},
   132  			"instances": &schema.Schema{
   133  				Type:     schema.TypeList,
   134  				Computed: true,
   135  				Elem:     &schema.Schema{Type: schema.TypeString},
   136  			},
   137  			"launch_configurations": &schema.Schema{
   138  				Type:     schema.TypeList,
   139  				Computed: true,
   140  				Elem:     &schema.Schema{Type: schema.TypeString},
   141  			},
   142  			"load_balancers": &schema.Schema{
   143  				Type:     schema.TypeList,
   144  				Computed: true,
   145  				Elem:     &schema.Schema{Type: schema.TypeString},
   146  			},
   147  			"queues": &schema.Schema{
   148  				Type:     schema.TypeList,
   149  				Computed: true,
   150  				Elem:     &schema.Schema{Type: schema.TypeString},
   151  			},
   152  			"triggers": &schema.Schema{
   153  				Type:     schema.TypeList,
   154  				Computed: true,
   155  				Elem:     &schema.Schema{Type: schema.TypeString},
   156  			},
   157  
   158  			"tags": tagsSchema(),
   159  		},
   160  	}
   161  }
   162  
   163  func resourceAwsElasticBeanstalkEnvironmentCreate(d *schema.ResourceData, meta interface{}) error {
   164  	conn := meta.(*AWSClient).elasticbeanstalkconn
   165  
   166  	// Get values from config
   167  	name := d.Get("name").(string)
   168  	cnamePrefix := d.Get("cname_prefix").(string)
   169  	tier := d.Get("tier").(string)
   170  	app := d.Get("application").(string)
   171  	desc := d.Get("description").(string)
   172  	settings := d.Get("setting").(*schema.Set)
   173  	solutionStack := d.Get("solution_stack_name").(string)
   174  	templateName := d.Get("template_name").(string)
   175  	waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string))
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	// TODO set tags
   181  	// Note: at time of writing, you cannot view or edit Tags after creation
   182  	// d.Set("tags", tagsToMap(instance.Tags))
   183  	createOpts := elasticbeanstalk.CreateEnvironmentInput{
   184  		EnvironmentName: aws.String(name),
   185  		ApplicationName: aws.String(app),
   186  		OptionSettings:  extractOptionSettings(settings),
   187  		Tags:            tagsFromMapBeanstalk(d.Get("tags").(map[string]interface{})),
   188  	}
   189  
   190  	if desc != "" {
   191  		createOpts.Description = aws.String(desc)
   192  	}
   193  
   194  	if cnamePrefix != "" {
   195  		if tier != "WebServer" {
   196  			return fmt.Errorf("Cannont set cname_prefix for tier: %s.", tier)
   197  		}
   198  		createOpts.CNAMEPrefix = aws.String(cnamePrefix)
   199  	}
   200  
   201  	if tier != "" {
   202  		var tierType string
   203  
   204  		switch tier {
   205  		case "WebServer":
   206  			tierType = "Standard"
   207  		case "Worker":
   208  			tierType = "SQS/HTTP"
   209  		}
   210  		environmentTier := elasticbeanstalk.EnvironmentTier{
   211  			Name: aws.String(tier),
   212  			Type: aws.String(tierType),
   213  		}
   214  		createOpts.Tier = &environmentTier
   215  	}
   216  
   217  	if solutionStack != "" {
   218  		createOpts.SolutionStackName = aws.String(solutionStack)
   219  	}
   220  
   221  	if templateName != "" {
   222  		createOpts.TemplateName = aws.String(templateName)
   223  	}
   224  
   225  	log.Printf("[DEBUG] Elastic Beanstalk Environment create opts: %s", createOpts)
   226  	resp, err := conn.CreateEnvironment(&createOpts)
   227  	if err != nil {
   228  		return err
   229  	}
   230  
   231  	// Assign the application name as the resource ID
   232  	d.SetId(*resp.EnvironmentId)
   233  
   234  	stateConf := &resource.StateChangeConf{
   235  		Pending:    []string{"Launching", "Updating"},
   236  		Target:     []string{"Ready"},
   237  		Refresh:    environmentStateRefreshFunc(conn, d.Id()),
   238  		Timeout:    waitForReadyTimeOut,
   239  		Delay:      10 * time.Second,
   240  		MinTimeout: 3 * time.Second,
   241  	}
   242  
   243  	_, err = stateConf.WaitForState()
   244  	if err != nil {
   245  		return fmt.Errorf(
   246  			"Error waiting for Elastic Beanstalk Environment (%s) to become ready: %s",
   247  			d.Id(), err)
   248  	}
   249  
   250  	return resourceAwsElasticBeanstalkEnvironmentRead(d, meta)
   251  }
   252  
   253  func resourceAwsElasticBeanstalkEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error {
   254  	conn := meta.(*AWSClient).elasticbeanstalkconn
   255  
   256  	if d.HasChange("description") {
   257  		if err := resourceAwsElasticBeanstalkEnvironmentDescriptionUpdate(conn, d); err != nil {
   258  			return err
   259  		}
   260  	}
   261  
   262  	if d.HasChange("solution_stack_name") {
   263  		if err := resourceAwsElasticBeanstalkEnvironmentSolutionStackUpdate(conn, d); err != nil {
   264  			return err
   265  		}
   266  	}
   267  
   268  	if d.HasChange("setting") {
   269  		if err := resourceAwsElasticBeanstalkEnvironmentOptionSettingsUpdate(conn, d); err != nil {
   270  			return err
   271  		}
   272  	}
   273  
   274  	return resourceAwsElasticBeanstalkEnvironmentRead(d, meta)
   275  }
   276  
   277  func resourceAwsElasticBeanstalkEnvironmentDescriptionUpdate(conn *elasticbeanstalk.ElasticBeanstalk, d *schema.ResourceData) error {
   278  	name := d.Get("name").(string)
   279  	desc := d.Get("description").(string)
   280  	envId := d.Id()
   281  
   282  	log.Printf("[DEBUG] Elastic Beanstalk application: %s, update description: %s", name, desc)
   283  
   284  	_, err := conn.UpdateEnvironment(&elasticbeanstalk.UpdateEnvironmentInput{
   285  		EnvironmentId: aws.String(envId),
   286  		Description:   aws.String(desc),
   287  	})
   288  
   289  	return err
   290  }
   291  
   292  func resourceAwsElasticBeanstalkEnvironmentOptionSettingsUpdate(conn *elasticbeanstalk.ElasticBeanstalk, d *schema.ResourceData) error {
   293  	name := d.Get("name").(string)
   294  	envId := d.Id()
   295  
   296  	log.Printf("[DEBUG] Elastic Beanstalk application: %s, update options", name)
   297  
   298  	req := &elasticbeanstalk.UpdateEnvironmentInput{
   299  		EnvironmentId: aws.String(envId),
   300  	}
   301  
   302  	if d.HasChange("setting") {
   303  		o, n := d.GetChange("setting")
   304  		if o == nil {
   305  			o = &schema.Set{F: optionSettingValueHash}
   306  		}
   307  		if n == nil {
   308  			n = &schema.Set{F: optionSettingValueHash}
   309  		}
   310  
   311  		os := o.(*schema.Set)
   312  		ns := n.(*schema.Set)
   313  
   314  		req.OptionSettings = extractOptionSettings(ns.Difference(os))
   315  	}
   316  
   317  	if _, err := conn.UpdateEnvironment(req); err != nil {
   318  		return err
   319  	}
   320  
   321  	return nil
   322  }
   323  
   324  func resourceAwsElasticBeanstalkEnvironmentSolutionStackUpdate(conn *elasticbeanstalk.ElasticBeanstalk, d *schema.ResourceData) error {
   325  	name := d.Get("name").(string)
   326  	solutionStack := d.Get("solution_stack_name").(string)
   327  	envId := d.Id()
   328  
   329  	log.Printf("[DEBUG] Elastic Beanstalk application: %s, update solution_stack_name: %s", name, solutionStack)
   330  
   331  	_, err := conn.UpdateEnvironment(&elasticbeanstalk.UpdateEnvironmentInput{
   332  		EnvironmentId:     aws.String(envId),
   333  		SolutionStackName: aws.String(solutionStack),
   334  	})
   335  
   336  	return err
   337  }
   338  
   339  func resourceAwsElasticBeanstalkEnvironmentRead(d *schema.ResourceData, meta interface{}) error {
   340  	conn := meta.(*AWSClient).elasticbeanstalkconn
   341  
   342  	app := d.Get("application").(string)
   343  	envId := d.Id()
   344  	tier := d.Get("tier").(string)
   345  
   346  	log.Printf("[DEBUG] Elastic Beanstalk environment read %s: id %s", d.Get("name").(string), d.Id())
   347  
   348  	resp, err := conn.DescribeEnvironments(&elasticbeanstalk.DescribeEnvironmentsInput{
   349  		ApplicationName: aws.String(app),
   350  		EnvironmentIds:  []*string{aws.String(envId)},
   351  	})
   352  
   353  	if err != nil {
   354  		return err
   355  	}
   356  
   357  	if len(resp.Environments) == 0 {
   358  		log.Printf("[DEBUG] Elastic Beanstalk environment properties: could not find environment %s", d.Id())
   359  
   360  		d.SetId("")
   361  		return nil
   362  	} else if len(resp.Environments) != 1 {
   363  		return fmt.Errorf("Error reading application properties: found %d environments, expected 1", len(resp.Environments))
   364  	}
   365  
   366  	env := resp.Environments[0]
   367  
   368  	if *env.Status == "Terminated" {
   369  		log.Printf("[DEBUG] Elastic Beanstalk environment %s was terminated", d.Id())
   370  
   371  		d.SetId("")
   372  		return nil
   373  	}
   374  
   375  	resources, err := conn.DescribeEnvironmentResources(&elasticbeanstalk.DescribeEnvironmentResourcesInput{
   376  		EnvironmentId: aws.String(envId),
   377  	})
   378  
   379  	if err != nil {
   380  		return err
   381  	}
   382  
   383  	if err := d.Set("description", env.Description); err != nil {
   384  		return err
   385  	}
   386  
   387  	if err := d.Set("cname", env.CNAME); err != nil {
   388  		return err
   389  	}
   390  
   391  	if tier == "WebServer" {
   392  		beanstalkCnamePrefixRegexp := regexp.MustCompile(`(^[^.]+).\w{2}-\w{4}-\d.elasticbeanstalk.com$`)
   393  		var cnamePrefix string
   394  		cnamePrefixMatch := beanstalkCnamePrefixRegexp.FindStringSubmatch(*env.CNAME)
   395  
   396  		if cnamePrefixMatch == nil {
   397  			cnamePrefix = ""
   398  		} else {
   399  			cnamePrefix = cnamePrefixMatch[1]
   400  		}
   401  
   402  		if err := d.Set("cname_prefix", cnamePrefix); err != nil {
   403  			return err
   404  		}
   405  	}
   406  
   407  	if err := d.Set("autoscaling_groups", flattenBeanstalkAsg(resources.EnvironmentResources.AutoScalingGroups)); err != nil {
   408  		return err
   409  	}
   410  
   411  	if err := d.Set("instances", flattenBeanstalkInstances(resources.EnvironmentResources.Instances)); err != nil {
   412  		return err
   413  	}
   414  	if err := d.Set("launch_configurations", flattenBeanstalkLc(resources.EnvironmentResources.LaunchConfigurations)); err != nil {
   415  		return err
   416  	}
   417  	if err := d.Set("load_balancers", flattenBeanstalkElb(resources.EnvironmentResources.LoadBalancers)); err != nil {
   418  		return err
   419  	}
   420  	if err := d.Set("queues", flattenBeanstalkSqs(resources.EnvironmentResources.Queues)); err != nil {
   421  		return err
   422  	}
   423  	if err := d.Set("triggers", flattenBeanstalkTrigger(resources.EnvironmentResources.Triggers)); err != nil {
   424  		return err
   425  	}
   426  
   427  	return resourceAwsElasticBeanstalkEnvironmentSettingsRead(d, meta)
   428  }
   429  
   430  func fetchAwsElasticBeanstalkEnvironmentSettings(d *schema.ResourceData, meta interface{}) (*schema.Set, error) {
   431  	conn := meta.(*AWSClient).elasticbeanstalkconn
   432  
   433  	app := d.Get("application").(string)
   434  	name := d.Get("name").(string)
   435  
   436  	resp, err := conn.DescribeConfigurationSettings(&elasticbeanstalk.DescribeConfigurationSettingsInput{
   437  		ApplicationName: aws.String(app),
   438  		EnvironmentName: aws.String(name),
   439  	})
   440  
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  
   445  	if len(resp.ConfigurationSettings) != 1 {
   446  		return nil, fmt.Errorf("Error reading environment settings: received %d settings groups, expected 1", len(resp.ConfigurationSettings))
   447  	}
   448  
   449  	settings := &schema.Set{F: optionSettingValueHash}
   450  	for _, optionSetting := range resp.ConfigurationSettings[0].OptionSettings {
   451  		m := map[string]interface{}{}
   452  
   453  		if optionSetting.Namespace != nil {
   454  			m["namespace"] = *optionSetting.Namespace
   455  		} else {
   456  			return nil, fmt.Errorf("Error reading environment settings: option setting with no namespace: %v", optionSetting)
   457  		}
   458  
   459  		if optionSetting.OptionName != nil {
   460  			m["name"] = *optionSetting.OptionName
   461  		} else {
   462  			return nil, fmt.Errorf("Error reading environment settings: option setting with no name: %v", optionSetting)
   463  		}
   464  
   465  		if optionSetting.Value != nil {
   466  			switch *optionSetting.OptionName {
   467  			case "SecurityGroups":
   468  				m["value"] = dropGeneratedSecurityGroup(*optionSetting.Value, meta)
   469  			case "Subnets", "ELBSubnets":
   470  				m["value"] = sortValues(*optionSetting.Value)
   471  			default:
   472  				m["value"] = *optionSetting.Value
   473  			}
   474  		}
   475  
   476  		settings.Add(m)
   477  	}
   478  
   479  	return settings, nil
   480  }
   481  
   482  func resourceAwsElasticBeanstalkEnvironmentSettingsRead(d *schema.ResourceData, meta interface{}) error {
   483  	log.Printf("[DEBUG] Elastic Beanstalk environment settings read %s: id %s", d.Get("name").(string), d.Id())
   484  
   485  	allSettings, err := fetchAwsElasticBeanstalkEnvironmentSettings(d, meta)
   486  	if err != nil {
   487  		return err
   488  	}
   489  
   490  	settings := d.Get("setting").(*schema.Set)
   491  
   492  	log.Printf("[DEBUG] Elastic Beanstalk allSettings: %s", allSettings.GoString())
   493  	log.Printf("[DEBUG] Elastic Beanstalk settings: %s", settings.GoString())
   494  
   495  	// perform the set operation with only name/namespace as keys, excluding value
   496  	// this is so we override things in the settings resource data key with updated values
   497  	// from the api.  we skip values we didn't know about before because there are so many
   498  	// defaults set by the eb api that we would delete many useful defaults.
   499  	//
   500  	// there is likely a better way to do this
   501  	allSettingsKeySet := schema.NewSet(optionSettingKeyHash, allSettings.List())
   502  	settingsKeySet := schema.NewSet(optionSettingKeyHash, settings.List())
   503  	updatedSettingsKeySet := allSettingsKeySet.Intersection(settingsKeySet)
   504  
   505  	log.Printf("[DEBUG] Elastic Beanstalk updatedSettingsKeySet: %s", updatedSettingsKeySet.GoString())
   506  
   507  	updatedSettings := schema.NewSet(optionSettingValueHash, updatedSettingsKeySet.List())
   508  
   509  	log.Printf("[DEBUG] Elastic Beanstalk updatedSettings: %s", updatedSettings.GoString())
   510  
   511  	if err := d.Set("all_settings", allSettings.List()); err != nil {
   512  		return err
   513  	}
   514  
   515  	if err := d.Set("setting", updatedSettings.List()); err != nil {
   516  		return err
   517  	}
   518  
   519  	return nil
   520  }
   521  
   522  func resourceAwsElasticBeanstalkEnvironmentDelete(d *schema.ResourceData, meta interface{}) error {
   523  	conn := meta.(*AWSClient).elasticbeanstalkconn
   524  
   525  	waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string))
   526  	if err != nil {
   527  		return err
   528  	}
   529  
   530  	opts := elasticbeanstalk.TerminateEnvironmentInput{
   531  		EnvironmentId:      aws.String(d.Id()),
   532  		TerminateResources: aws.Bool(true),
   533  	}
   534  
   535  	log.Printf("[DEBUG] Elastic Beanstalk Environment terminate opts: %s", opts)
   536  	_, err = conn.TerminateEnvironment(&opts)
   537  
   538  	if err != nil {
   539  		return err
   540  	}
   541  
   542  	stateConf := &resource.StateChangeConf{
   543  		Pending:    []string{"Terminating"},
   544  		Target:     []string{"Terminated"},
   545  		Refresh:    environmentStateRefreshFunc(conn, d.Id()),
   546  		Timeout:    waitForReadyTimeOut,
   547  		Delay:      10 * time.Second,
   548  		MinTimeout: 3 * time.Second,
   549  	}
   550  
   551  	_, err = stateConf.WaitForState()
   552  	if err != nil {
   553  		return fmt.Errorf(
   554  			"Error waiting for Elastic Beanstalk Environment (%s) to become terminated: %s",
   555  			d.Id(), err)
   556  	}
   557  
   558  	return nil
   559  }
   560  
   561  // environmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   562  // the creation of the Beanstalk Environment
   563  func environmentStateRefreshFunc(conn *elasticbeanstalk.ElasticBeanstalk, environmentId string) resource.StateRefreshFunc {
   564  	return func() (interface{}, string, error) {
   565  		resp, err := conn.DescribeEnvironments(&elasticbeanstalk.DescribeEnvironmentsInput{
   566  			EnvironmentIds: []*string{aws.String(environmentId)},
   567  		})
   568  		if err != nil {
   569  			log.Printf("[Err] Error waiting for Elastic Beanstalk Environment state: %s", err)
   570  			return -1, "failed", fmt.Errorf("[Err] Error waiting for Elastic Beanstalk Environment state: %s", err)
   571  		}
   572  
   573  		if resp == nil || len(resp.Environments) == 0 {
   574  			// Sometimes AWS just has consistency issues and doesn't see
   575  			// our instance yet. Return an empty state.
   576  			return nil, "", nil
   577  		}
   578  
   579  		var env *elasticbeanstalk.EnvironmentDescription
   580  		for _, e := range resp.Environments {
   581  			if environmentId == *e.EnvironmentId {
   582  				env = e
   583  			}
   584  		}
   585  
   586  		if env == nil {
   587  			return -1, "failed", fmt.Errorf("[Err] Error finding Elastic Beanstalk Environment, environment not found")
   588  		}
   589  
   590  		return env, *env.Status, nil
   591  	}
   592  }
   593  
   594  // we use the following two functions to allow us to split out defaults
   595  // as they become overridden from within the template
   596  func optionSettingValueHash(v interface{}) int {
   597  	rd := v.(map[string]interface{})
   598  	namespace := rd["namespace"].(string)
   599  	optionName := rd["name"].(string)
   600  	value, _ := rd["value"].(string)
   601  	hk := fmt.Sprintf("%s:%s=%s", namespace, optionName, sortValues(value))
   602  	log.Printf("[DEBUG] Elastic Beanstalk optionSettingValueHash(%#v): %s: hk=%s,hc=%d", v, optionName, hk, hashcode.String(hk))
   603  	return hashcode.String(hk)
   604  }
   605  
   606  func optionSettingKeyHash(v interface{}) int {
   607  	rd := v.(map[string]interface{})
   608  	namespace := rd["namespace"].(string)
   609  	optionName := rd["name"].(string)
   610  	hk := fmt.Sprintf("%s:%s", namespace, optionName)
   611  	log.Printf("[DEBUG] Elastic Beanstalk optionSettingKeyHash(%#v): %s: hk=%s,hc=%d", v, optionName, hk, hashcode.String(hk))
   612  	return hashcode.String(hk)
   613  }
   614  
   615  func sortValues(v string) string {
   616  	values := strings.Split(v, ",")
   617  	sort.Strings(values)
   618  	return strings.Join(values, ",")
   619  }
   620  
   621  func extractOptionSettings(s *schema.Set) []*elasticbeanstalk.ConfigurationOptionSetting {
   622  	settings := []*elasticbeanstalk.ConfigurationOptionSetting{}
   623  
   624  	if s != nil {
   625  		for _, setting := range s.List() {
   626  			settings = append(settings, &elasticbeanstalk.ConfigurationOptionSetting{
   627  				Namespace:  aws.String(setting.(map[string]interface{})["namespace"].(string)),
   628  				OptionName: aws.String(setting.(map[string]interface{})["name"].(string)),
   629  				Value:      aws.String(setting.(map[string]interface{})["value"].(string)),
   630  			})
   631  		}
   632  	}
   633  
   634  	return settings
   635  }
   636  
   637  func dropGeneratedSecurityGroup(settingValue string, meta interface{}) string {
   638  	conn := meta.(*AWSClient).ec2conn
   639  
   640  	groups := strings.Split(settingValue, ",")
   641  
   642  	resp, err := conn.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{
   643  		GroupIds: aws.StringSlice(groups),
   644  	})
   645  
   646  	if err != nil {
   647  		log.Printf("[DEBUG] Elastic Beanstalk error describing SecurityGroups: %v", err)
   648  		return settingValue
   649  	}
   650  
   651  	var legitGroups []string
   652  	for _, group := range resp.SecurityGroups {
   653  		log.Printf("[DEBUG] Elastic Beanstalk SecurityGroup: %v", *group.GroupName)
   654  		if !strings.HasPrefix(*group.GroupName, "awseb") {
   655  			legitGroups = append(legitGroups, *group.GroupId)
   656  		}
   657  	}
   658  
   659  	sort.Strings(legitGroups)
   660  
   661  	return strings.Join(legitGroups, ",")
   662  }