github.com/profects/terraform@v0.9.0-beta1.0.20170227135739-92d4809db30d/builtin/providers/aws/resource_aws_instance.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha1"
     6  	"encoding/base64"
     7  	"encoding/hex"
     8  	"errors"
     9  	"fmt"
    10  	"log"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/aws/aws-sdk-go/aws"
    15  	"github.com/aws/aws-sdk-go/aws/awserr"
    16  	"github.com/aws/aws-sdk-go/service/ec2"
    17  	"github.com/hashicorp/terraform/helper/hashcode"
    18  	"github.com/hashicorp/terraform/helper/resource"
    19  	"github.com/hashicorp/terraform/helper/schema"
    20  )
    21  
    22  func resourceAwsInstance() *schema.Resource {
    23  	return &schema.Resource{
    24  		Create: resourceAwsInstanceCreate,
    25  		Read:   resourceAwsInstanceRead,
    26  		Update: resourceAwsInstanceUpdate,
    27  		Delete: resourceAwsInstanceDelete,
    28  		Importer: &schema.ResourceImporter{
    29  			State: schema.ImportStatePassthrough,
    30  		},
    31  
    32  		SchemaVersion: 1,
    33  		MigrateState:  resourceAwsInstanceMigrateState,
    34  
    35  		Schema: map[string]*schema.Schema{
    36  			"ami": {
    37  				Type:     schema.TypeString,
    38  				Required: true,
    39  				ForceNew: true,
    40  			},
    41  
    42  			"associate_public_ip_address": {
    43  				Type:     schema.TypeBool,
    44  				ForceNew: true,
    45  				Computed: true,
    46  				Optional: true,
    47  			},
    48  
    49  			"availability_zone": {
    50  				Type:     schema.TypeString,
    51  				Optional: true,
    52  				Computed: true,
    53  				ForceNew: true,
    54  			},
    55  
    56  			"placement_group": {
    57  				Type:     schema.TypeString,
    58  				Optional: true,
    59  				Computed: true,
    60  				ForceNew: true,
    61  			},
    62  
    63  			"instance_type": {
    64  				Type:     schema.TypeString,
    65  				Required: true,
    66  			},
    67  
    68  			"key_name": {
    69  				Type:     schema.TypeString,
    70  				Optional: true,
    71  				ForceNew: true,
    72  				Computed: true,
    73  			},
    74  
    75  			"subnet_id": {
    76  				Type:     schema.TypeString,
    77  				Optional: true,
    78  				Computed: true,
    79  				ForceNew: true,
    80  			},
    81  
    82  			"private_ip": {
    83  				Type:     schema.TypeString,
    84  				Optional: true,
    85  				ForceNew: true,
    86  				Computed: true,
    87  			},
    88  
    89  			"source_dest_check": {
    90  				Type:     schema.TypeBool,
    91  				Optional: true,
    92  				Default:  true,
    93  			},
    94  
    95  			"user_data": {
    96  				Type:     schema.TypeString,
    97  				Optional: true,
    98  				ForceNew: true,
    99  				StateFunc: func(v interface{}) string {
   100  					switch v.(type) {
   101  					case string:
   102  						return userDataHashSum(v.(string))
   103  					default:
   104  						return ""
   105  					}
   106  				},
   107  			},
   108  
   109  			"security_groups": {
   110  				Type:     schema.TypeSet,
   111  				Optional: true,
   112  				Computed: true,
   113  				ForceNew: true,
   114  				Elem:     &schema.Schema{Type: schema.TypeString},
   115  				Set:      schema.HashString,
   116  			},
   117  
   118  			"vpc_security_group_ids": {
   119  				Type:     schema.TypeSet,
   120  				Optional: true,
   121  				Computed: true,
   122  				Elem:     &schema.Schema{Type: schema.TypeString},
   123  				Set:      schema.HashString,
   124  			},
   125  
   126  			"public_dns": {
   127  				Type:     schema.TypeString,
   128  				Computed: true,
   129  			},
   130  
   131  			"network_interface_id": {
   132  				Type:     schema.TypeString,
   133  				Computed: true,
   134  			},
   135  
   136  			"public_ip": {
   137  				Type:     schema.TypeString,
   138  				Computed: true,
   139  			},
   140  
   141  			"instance_state": {
   142  				Type:     schema.TypeString,
   143  				Computed: true,
   144  			},
   145  
   146  			"private_dns": {
   147  				Type:     schema.TypeString,
   148  				Computed: true,
   149  			},
   150  
   151  			"ebs_optimized": {
   152  				Type:     schema.TypeBool,
   153  				Optional: true,
   154  				ForceNew: true,
   155  			},
   156  
   157  			"disable_api_termination": {
   158  				Type:     schema.TypeBool,
   159  				Optional: true,
   160  			},
   161  
   162  			"instance_initiated_shutdown_behavior": {
   163  				Type:     schema.TypeString,
   164  				Optional: true,
   165  			},
   166  
   167  			"monitoring": {
   168  				Type:     schema.TypeBool,
   169  				Optional: true,
   170  			},
   171  
   172  			"iam_instance_profile": {
   173  				Type:     schema.TypeString,
   174  				ForceNew: true,
   175  				Optional: true,
   176  			},
   177  
   178  			"tenancy": {
   179  				Type:     schema.TypeString,
   180  				Optional: true,
   181  				Computed: true,
   182  				ForceNew: true,
   183  			},
   184  
   185  			"tags": tagsSchema(),
   186  
   187  			"block_device": {
   188  				Type:     schema.TypeMap,
   189  				Optional: true,
   190  				Removed:  "Split out into three sub-types; see Changelog and Docs",
   191  			},
   192  
   193  			"ebs_block_device": {
   194  				Type:     schema.TypeSet,
   195  				Optional: true,
   196  				Computed: true,
   197  				Elem: &schema.Resource{
   198  					Schema: map[string]*schema.Schema{
   199  						"delete_on_termination": {
   200  							Type:     schema.TypeBool,
   201  							Optional: true,
   202  							Default:  true,
   203  							ForceNew: true,
   204  						},
   205  
   206  						"device_name": {
   207  							Type:     schema.TypeString,
   208  							Required: true,
   209  							ForceNew: true,
   210  						},
   211  
   212  						"encrypted": {
   213  							Type:     schema.TypeBool,
   214  							Optional: true,
   215  							Computed: true,
   216  							ForceNew: true,
   217  						},
   218  
   219  						"iops": {
   220  							Type:     schema.TypeInt,
   221  							Optional: true,
   222  							Computed: true,
   223  							ForceNew: true,
   224  						},
   225  
   226  						"snapshot_id": {
   227  							Type:     schema.TypeString,
   228  							Optional: true,
   229  							Computed: true,
   230  							ForceNew: true,
   231  						},
   232  
   233  						"volume_size": {
   234  							Type:     schema.TypeInt,
   235  							Optional: true,
   236  							Computed: true,
   237  							ForceNew: true,
   238  						},
   239  
   240  						"volume_type": {
   241  							Type:     schema.TypeString,
   242  							Optional: true,
   243  							Computed: true,
   244  							ForceNew: true,
   245  						},
   246  					},
   247  				},
   248  				Set: func(v interface{}) int {
   249  					var buf bytes.Buffer
   250  					m := v.(map[string]interface{})
   251  					buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
   252  					buf.WriteString(fmt.Sprintf("%s-", m["snapshot_id"].(string)))
   253  					return hashcode.String(buf.String())
   254  				},
   255  			},
   256  
   257  			"ephemeral_block_device": {
   258  				Type:     schema.TypeSet,
   259  				Optional: true,
   260  				Computed: true,
   261  				ForceNew: true,
   262  				Elem: &schema.Resource{
   263  					Schema: map[string]*schema.Schema{
   264  						"device_name": {
   265  							Type:     schema.TypeString,
   266  							Required: true,
   267  						},
   268  
   269  						"virtual_name": {
   270  							Type:     schema.TypeString,
   271  							Optional: true,
   272  						},
   273  
   274  						"no_device": {
   275  							Type:     schema.TypeBool,
   276  							Optional: true,
   277  						},
   278  					},
   279  				},
   280  				Set: func(v interface{}) int {
   281  					var buf bytes.Buffer
   282  					m := v.(map[string]interface{})
   283  					buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
   284  					buf.WriteString(fmt.Sprintf("%s-", m["virtual_name"].(string)))
   285  					if v, ok := m["no_device"].(bool); ok && v {
   286  						buf.WriteString(fmt.Sprintf("%t-", v))
   287  					}
   288  					return hashcode.String(buf.String())
   289  				},
   290  			},
   291  
   292  			"root_block_device": {
   293  				Type:     schema.TypeList,
   294  				Optional: true,
   295  				Computed: true,
   296  				MaxItems: 1,
   297  				Elem: &schema.Resource{
   298  					// "You can only modify the volume size, volume type, and Delete on
   299  					// Termination flag on the block device mapping entry for the root
   300  					// device volume." - bit.ly/ec2bdmap
   301  					Schema: map[string]*schema.Schema{
   302  						"delete_on_termination": {
   303  							Type:     schema.TypeBool,
   304  							Optional: true,
   305  							Default:  true,
   306  							ForceNew: true,
   307  						},
   308  
   309  						"iops": {
   310  							Type:     schema.TypeInt,
   311  							Optional: true,
   312  							Computed: true,
   313  							ForceNew: true,
   314  						},
   315  
   316  						"volume_size": {
   317  							Type:     schema.TypeInt,
   318  							Optional: true,
   319  							Computed: true,
   320  							ForceNew: true,
   321  						},
   322  
   323  						"volume_type": {
   324  							Type:     schema.TypeString,
   325  							Optional: true,
   326  							Computed: true,
   327  							ForceNew: true,
   328  						},
   329  					},
   330  				},
   331  			},
   332  		},
   333  	}
   334  }
   335  
   336  func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   337  	conn := meta.(*AWSClient).ec2conn
   338  
   339  	instanceOpts, err := buildAwsInstanceOpts(d, meta)
   340  	if err != nil {
   341  		return err
   342  	}
   343  
   344  	// Build the creation struct
   345  	runOpts := &ec2.RunInstancesInput{
   346  		BlockDeviceMappings:   instanceOpts.BlockDeviceMappings,
   347  		DisableApiTermination: instanceOpts.DisableAPITermination,
   348  		EbsOptimized:          instanceOpts.EBSOptimized,
   349  		Monitoring:            instanceOpts.Monitoring,
   350  		IamInstanceProfile:    instanceOpts.IAMInstanceProfile,
   351  		ImageId:               instanceOpts.ImageID,
   352  		InstanceInitiatedShutdownBehavior: instanceOpts.InstanceInitiatedShutdownBehavior,
   353  		InstanceType:                      instanceOpts.InstanceType,
   354  		KeyName:                           instanceOpts.KeyName,
   355  		MaxCount:                          aws.Int64(int64(1)),
   356  		MinCount:                          aws.Int64(int64(1)),
   357  		NetworkInterfaces:                 instanceOpts.NetworkInterfaces,
   358  		Placement:                         instanceOpts.Placement,
   359  		PrivateIpAddress:                  instanceOpts.PrivateIPAddress,
   360  		SecurityGroupIds:                  instanceOpts.SecurityGroupIDs,
   361  		SecurityGroups:                    instanceOpts.SecurityGroups,
   362  		SubnetId:                          instanceOpts.SubnetID,
   363  		UserData:                          instanceOpts.UserData64,
   364  	}
   365  
   366  	// Create the instance
   367  	log.Printf("[DEBUG] Run configuration: %s", runOpts)
   368  
   369  	var runResp *ec2.Reservation
   370  	err = resource.Retry(15*time.Second, func() *resource.RetryError {
   371  		var err error
   372  		runResp, err = conn.RunInstances(runOpts)
   373  		// IAM instance profiles can take ~10 seconds to propagate in AWS:
   374  		// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
   375  		if isAWSErr(err, "InvalidParameterValue", "Invalid IAM Instance Profile") {
   376  			log.Print("[DEBUG] Invalid IAM Instance Profile referenced, retrying...")
   377  			return resource.RetryableError(err)
   378  		}
   379  		// IAM roles can also take time to propagate in AWS:
   380  		if isAWSErr(err, "InvalidParameterValue", " has no associated IAM Roles") {
   381  			log.Print("[DEBUG] IAM Instance Profile appears to have no IAM roles, retrying...")
   382  			return resource.RetryableError(err)
   383  		}
   384  		return resource.NonRetryableError(err)
   385  	})
   386  	// Warn if the AWS Error involves group ids, to help identify situation
   387  	// where a user uses group ids in security_groups for the Default VPC.
   388  	//   See https://github.com/hashicorp/terraform/issues/3798
   389  	if isAWSErr(err, "InvalidParameterValue", "groupId is invalid") {
   390  		return fmt.Errorf("Error launching instance, possible mismatch of Security Group IDs and Names. See AWS Instance docs here: %s.\n\n\tAWS Error: %s", "https://terraform.io/docs/providers/aws/r/instance.html", err.(awserr.Error).Message())
   391  	}
   392  	if err != nil {
   393  		return fmt.Errorf("Error launching source instance: %s", err)
   394  	}
   395  	if runResp == nil || len(runResp.Instances) == 0 {
   396  		return errors.New("Error launching source instance: no instances returned in response")
   397  	}
   398  
   399  	instance := runResp.Instances[0]
   400  	log.Printf("[INFO] Instance ID: %s", *instance.InstanceId)
   401  
   402  	// Store the resulting ID so we can look this up later
   403  	d.SetId(*instance.InstanceId)
   404  
   405  	// Wait for the instance to become running so we can get some attributes
   406  	// that aren't available until later.
   407  	log.Printf(
   408  		"[DEBUG] Waiting for instance (%s) to become running",
   409  		*instance.InstanceId)
   410  
   411  	stateConf := &resource.StateChangeConf{
   412  		Pending:    []string{"pending"},
   413  		Target:     []string{"running"},
   414  		Refresh:    InstanceStateRefreshFunc(conn, *instance.InstanceId),
   415  		Timeout:    10 * time.Minute,
   416  		Delay:      10 * time.Second,
   417  		MinTimeout: 3 * time.Second,
   418  	}
   419  
   420  	instanceRaw, err := stateConf.WaitForState()
   421  	if err != nil {
   422  		return fmt.Errorf(
   423  			"Error waiting for instance (%s) to become ready: %s",
   424  			*instance.InstanceId, err)
   425  	}
   426  
   427  	instance = instanceRaw.(*ec2.Instance)
   428  
   429  	// Initialize the connection info
   430  	if instance.PublicIpAddress != nil {
   431  		d.SetConnInfo(map[string]string{
   432  			"type": "ssh",
   433  			"host": *instance.PublicIpAddress,
   434  		})
   435  	} else if instance.PrivateIpAddress != nil {
   436  		d.SetConnInfo(map[string]string{
   437  			"type": "ssh",
   438  			"host": *instance.PrivateIpAddress,
   439  		})
   440  	}
   441  
   442  	// Update if we need to
   443  	return resourceAwsInstanceUpdate(d, meta)
   444  }
   445  
   446  func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
   447  	conn := meta.(*AWSClient).ec2conn
   448  
   449  	resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{
   450  		InstanceIds: []*string{aws.String(d.Id())},
   451  	})
   452  	if err != nil {
   453  		// If the instance was not found, return nil so that we can show
   454  		// that the instance is gone.
   455  		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" {
   456  			d.SetId("")
   457  			return nil
   458  		}
   459  
   460  		// Some other error, report it
   461  		return err
   462  	}
   463  
   464  	// If nothing was found, then return no state
   465  	if len(resp.Reservations) == 0 {
   466  		d.SetId("")
   467  		return nil
   468  	}
   469  
   470  	instance := resp.Reservations[0].Instances[0]
   471  
   472  	if instance.State != nil {
   473  		// If the instance is terminated, then it is gone
   474  		if *instance.State.Name == "terminated" {
   475  			d.SetId("")
   476  			return nil
   477  		}
   478  
   479  		d.Set("instance_state", instance.State.Name)
   480  	}
   481  
   482  	if instance.Placement != nil {
   483  		d.Set("availability_zone", instance.Placement.AvailabilityZone)
   484  	}
   485  	if instance.Placement.Tenancy != nil {
   486  		d.Set("tenancy", instance.Placement.Tenancy)
   487  	}
   488  
   489  	d.Set("ami", instance.ImageId)
   490  	d.Set("instance_type", instance.InstanceType)
   491  	d.Set("key_name", instance.KeyName)
   492  	d.Set("public_dns", instance.PublicDnsName)
   493  	d.Set("public_ip", instance.PublicIpAddress)
   494  	d.Set("private_dns", instance.PrivateDnsName)
   495  	d.Set("private_ip", instance.PrivateIpAddress)
   496  	d.Set("iam_instance_profile", iamInstanceProfileArnToName(instance.IamInstanceProfile))
   497  
   498  	if len(instance.NetworkInterfaces) > 0 {
   499  		for _, ni := range instance.NetworkInterfaces {
   500  			if *ni.Attachment.DeviceIndex == 0 {
   501  				d.Set("subnet_id", ni.SubnetId)
   502  				d.Set("network_interface_id", ni.NetworkInterfaceId)
   503  				d.Set("associate_public_ip_address", ni.Association != nil)
   504  			}
   505  		}
   506  	} else {
   507  		d.Set("subnet_id", instance.SubnetId)
   508  		d.Set("network_interface_id", "")
   509  	}
   510  	d.Set("ebs_optimized", instance.EbsOptimized)
   511  	if instance.SubnetId != nil && *instance.SubnetId != "" {
   512  		d.Set("source_dest_check", instance.SourceDestCheck)
   513  	}
   514  
   515  	if instance.Monitoring != nil && instance.Monitoring.State != nil {
   516  		monitoringState := *instance.Monitoring.State
   517  		d.Set("monitoring", monitoringState == "enabled" || monitoringState == "pending")
   518  	}
   519  
   520  	d.Set("tags", tagsToMap(instance.Tags))
   521  
   522  	if err := readSecurityGroups(d, instance); err != nil {
   523  		return err
   524  	}
   525  
   526  	if err := readBlockDevices(d, instance, conn); err != nil {
   527  		return err
   528  	}
   529  	if _, ok := d.GetOk("ephemeral_block_device"); !ok {
   530  		d.Set("ephemeral_block_device", []interface{}{})
   531  	}
   532  
   533  	// Instance attributes
   534  	{
   535  		attr, err := conn.DescribeInstanceAttribute(&ec2.DescribeInstanceAttributeInput{
   536  			Attribute:  aws.String("disableApiTermination"),
   537  			InstanceId: aws.String(d.Id()),
   538  		})
   539  		if err != nil {
   540  			return err
   541  		}
   542  		d.Set("disable_api_termination", attr.DisableApiTermination.Value)
   543  	}
   544  	{
   545  		attr, err := conn.DescribeInstanceAttribute(&ec2.DescribeInstanceAttributeInput{
   546  			Attribute:  aws.String(ec2.InstanceAttributeNameUserData),
   547  			InstanceId: aws.String(d.Id()),
   548  		})
   549  		if err != nil {
   550  			return err
   551  		}
   552  		if attr.UserData.Value != nil {
   553  			d.Set("user_data", userDataHashSum(*attr.UserData.Value))
   554  		}
   555  	}
   556  
   557  	return nil
   558  }
   559  
   560  func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   561  	conn := meta.(*AWSClient).ec2conn
   562  
   563  	d.Partial(true)
   564  	if err := setTags(conn, d); err != nil {
   565  		return err
   566  	} else {
   567  		d.SetPartial("tags")
   568  	}
   569  
   570  	if d.HasChange("source_dest_check") || d.IsNewResource() {
   571  		// SourceDestCheck can only be set on VPC instances	// AWS will return an error of InvalidParameterCombination if we attempt
   572  		// to modify the source_dest_check of an instance in EC2 Classic
   573  		log.Printf("[INFO] Modifying `source_dest_check` on Instance %s", d.Id())
   574  		_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   575  			InstanceId: aws.String(d.Id()),
   576  			SourceDestCheck: &ec2.AttributeBooleanValue{
   577  				Value: aws.Bool(d.Get("source_dest_check").(bool)),
   578  			},
   579  		})
   580  		if err != nil {
   581  			if ec2err, ok := err.(awserr.Error); ok {
   582  				// Toloerate InvalidParameterCombination error in Classic, otherwise
   583  				// return the error
   584  				if "InvalidParameterCombination" != ec2err.Code() {
   585  					return err
   586  				}
   587  				log.Printf("[WARN] Attempted to modify SourceDestCheck on non VPC instance: %s", ec2err.Message())
   588  			}
   589  		}
   590  	}
   591  
   592  	if d.HasChange("vpc_security_group_ids") {
   593  		var groups []*string
   594  		if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
   595  			for _, v := range v.List() {
   596  				groups = append(groups, aws.String(v.(string)))
   597  			}
   598  		}
   599  		_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   600  			InstanceId: aws.String(d.Id()),
   601  			Groups:     groups,
   602  		})
   603  		if err != nil {
   604  			return err
   605  		}
   606  	}
   607  
   608  	if d.HasChange("instance_type") && !d.IsNewResource() {
   609  		log.Printf("[INFO] Stopping Instance %q for instance_type change", d.Id())
   610  		_, err := conn.StopInstances(&ec2.StopInstancesInput{
   611  			InstanceIds: []*string{aws.String(d.Id())},
   612  		})
   613  
   614  		stateConf := &resource.StateChangeConf{
   615  			Pending:    []string{"pending", "running", "shutting-down", "stopped", "stopping"},
   616  			Target:     []string{"stopped"},
   617  			Refresh:    InstanceStateRefreshFunc(conn, d.Id()),
   618  			Timeout:    10 * time.Minute,
   619  			Delay:      10 * time.Second,
   620  			MinTimeout: 3 * time.Second,
   621  		}
   622  
   623  		_, err = stateConf.WaitForState()
   624  		if err != nil {
   625  			return fmt.Errorf(
   626  				"Error waiting for instance (%s) to stop: %s", d.Id(), err)
   627  		}
   628  
   629  		log.Printf("[INFO] Modifying instance type %s", d.Id())
   630  		_, err = conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   631  			InstanceId: aws.String(d.Id()),
   632  			InstanceType: &ec2.AttributeValue{
   633  				Value: aws.String(d.Get("instance_type").(string)),
   634  			},
   635  		})
   636  		if err != nil {
   637  			return err
   638  		}
   639  
   640  		log.Printf("[INFO] Starting Instance %q after instance_type change", d.Id())
   641  		_, err = conn.StartInstances(&ec2.StartInstancesInput{
   642  			InstanceIds: []*string{aws.String(d.Id())},
   643  		})
   644  
   645  		stateConf = &resource.StateChangeConf{
   646  			Pending:    []string{"pending", "stopped"},
   647  			Target:     []string{"running"},
   648  			Refresh:    InstanceStateRefreshFunc(conn, d.Id()),
   649  			Timeout:    10 * time.Minute,
   650  			Delay:      10 * time.Second,
   651  			MinTimeout: 3 * time.Second,
   652  		}
   653  
   654  		_, err = stateConf.WaitForState()
   655  		if err != nil {
   656  			return fmt.Errorf(
   657  				"Error waiting for instance (%s) to become ready: %s",
   658  				d.Id(), err)
   659  		}
   660  	}
   661  
   662  	if d.HasChange("disable_api_termination") {
   663  		_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   664  			InstanceId: aws.String(d.Id()),
   665  			DisableApiTermination: &ec2.AttributeBooleanValue{
   666  				Value: aws.Bool(d.Get("disable_api_termination").(bool)),
   667  			},
   668  		})
   669  		if err != nil {
   670  			return err
   671  		}
   672  	}
   673  
   674  	if d.HasChange("instance_initiated_shutdown_behavior") {
   675  		log.Printf("[INFO] Modifying instance %s", d.Id())
   676  		_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   677  			InstanceId: aws.String(d.Id()),
   678  			InstanceInitiatedShutdownBehavior: &ec2.AttributeValue{
   679  				Value: aws.String(d.Get("instance_initiated_shutdown_behavior").(string)),
   680  			},
   681  		})
   682  		if err != nil {
   683  			return err
   684  		}
   685  	}
   686  
   687  	if d.HasChange("monitoring") {
   688  		var mErr error
   689  		if d.Get("monitoring").(bool) {
   690  			log.Printf("[DEBUG] Enabling monitoring for Instance (%s)", d.Id())
   691  			_, mErr = conn.MonitorInstances(&ec2.MonitorInstancesInput{
   692  				InstanceIds: []*string{aws.String(d.Id())},
   693  			})
   694  		} else {
   695  			log.Printf("[DEBUG] Disabling monitoring for Instance (%s)", d.Id())
   696  			_, mErr = conn.UnmonitorInstances(&ec2.UnmonitorInstancesInput{
   697  				InstanceIds: []*string{aws.String(d.Id())},
   698  			})
   699  		}
   700  		if mErr != nil {
   701  			return fmt.Errorf("[WARN] Error updating Instance monitoring: %s", mErr)
   702  		}
   703  	}
   704  
   705  	// TODO(mitchellh): wait for the attributes we modified to
   706  	// persist the change...
   707  
   708  	d.Partial(false)
   709  
   710  	return resourceAwsInstanceRead(d, meta)
   711  }
   712  
   713  func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   714  	conn := meta.(*AWSClient).ec2conn
   715  
   716  	if err := awsTerminateInstance(conn, d.Id()); err != nil {
   717  		return err
   718  	}
   719  
   720  	d.SetId("")
   721  	return nil
   722  }
   723  
   724  // InstanceStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   725  // an EC2 instance.
   726  func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRefreshFunc {
   727  	return func() (interface{}, string, error) {
   728  		resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{
   729  			InstanceIds: []*string{aws.String(instanceID)},
   730  		})
   731  		if err != nil {
   732  			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" {
   733  				// Set this to nil as if we didn't find anything.
   734  				resp = nil
   735  			} else {
   736  				log.Printf("Error on InstanceStateRefresh: %s", err)
   737  				return nil, "", err
   738  			}
   739  		}
   740  
   741  		if resp == nil || len(resp.Reservations) == 0 || len(resp.Reservations[0].Instances) == 0 {
   742  			// Sometimes AWS just has consistency issues and doesn't see
   743  			// our instance yet. Return an empty state.
   744  			return nil, "", nil
   745  		}
   746  
   747  		i := resp.Reservations[0].Instances[0]
   748  		return i, *i.State.Name, nil
   749  	}
   750  }
   751  
   752  func readBlockDevices(d *schema.ResourceData, instance *ec2.Instance, conn *ec2.EC2) error {
   753  	ibds, err := readBlockDevicesFromInstance(instance, conn)
   754  	if err != nil {
   755  		return err
   756  	}
   757  
   758  	if err := d.Set("ebs_block_device", ibds["ebs"]); err != nil {
   759  		return err
   760  	}
   761  
   762  	// This handles the import case which needs to be defaulted to empty
   763  	if _, ok := d.GetOk("root_block_device"); !ok {
   764  		if err := d.Set("root_block_device", []interface{}{}); err != nil {
   765  			return err
   766  		}
   767  	}
   768  
   769  	if ibds["root"] != nil {
   770  		roots := []interface{}{ibds["root"]}
   771  		if err := d.Set("root_block_device", roots); err != nil {
   772  			return err
   773  		}
   774  	}
   775  
   776  	return nil
   777  }
   778  
   779  func readBlockDevicesFromInstance(instance *ec2.Instance, conn *ec2.EC2) (map[string]interface{}, error) {
   780  	blockDevices := make(map[string]interface{})
   781  	blockDevices["ebs"] = make([]map[string]interface{}, 0)
   782  	blockDevices["root"] = nil
   783  
   784  	instanceBlockDevices := make(map[string]*ec2.InstanceBlockDeviceMapping)
   785  	for _, bd := range instance.BlockDeviceMappings {
   786  		if bd.Ebs != nil {
   787  			instanceBlockDevices[*bd.Ebs.VolumeId] = bd
   788  		}
   789  	}
   790  
   791  	if len(instanceBlockDevices) == 0 {
   792  		return nil, nil
   793  	}
   794  
   795  	volIDs := make([]*string, 0, len(instanceBlockDevices))
   796  	for volID := range instanceBlockDevices {
   797  		volIDs = append(volIDs, aws.String(volID))
   798  	}
   799  
   800  	// Need to call DescribeVolumes to get volume_size and volume_type for each
   801  	// EBS block device
   802  	volResp, err := conn.DescribeVolumes(&ec2.DescribeVolumesInput{
   803  		VolumeIds: volIDs,
   804  	})
   805  	if err != nil {
   806  		return nil, err
   807  	}
   808  
   809  	for _, vol := range volResp.Volumes {
   810  		instanceBd := instanceBlockDevices[*vol.VolumeId]
   811  		bd := make(map[string]interface{})
   812  
   813  		if instanceBd.Ebs != nil && instanceBd.Ebs.DeleteOnTermination != nil {
   814  			bd["delete_on_termination"] = *instanceBd.Ebs.DeleteOnTermination
   815  		}
   816  		if vol.Size != nil {
   817  			bd["volume_size"] = *vol.Size
   818  		}
   819  		if vol.VolumeType != nil {
   820  			bd["volume_type"] = *vol.VolumeType
   821  		}
   822  		if vol.Iops != nil {
   823  			bd["iops"] = *vol.Iops
   824  		}
   825  
   826  		if blockDeviceIsRoot(instanceBd, instance) {
   827  			blockDevices["root"] = bd
   828  		} else {
   829  			if instanceBd.DeviceName != nil {
   830  				bd["device_name"] = *instanceBd.DeviceName
   831  			}
   832  			if vol.Encrypted != nil {
   833  				bd["encrypted"] = *vol.Encrypted
   834  			}
   835  			if vol.SnapshotId != nil {
   836  				bd["snapshot_id"] = *vol.SnapshotId
   837  			}
   838  
   839  			blockDevices["ebs"] = append(blockDevices["ebs"].([]map[string]interface{}), bd)
   840  		}
   841  	}
   842  
   843  	return blockDevices, nil
   844  }
   845  
   846  func blockDeviceIsRoot(bd *ec2.InstanceBlockDeviceMapping, instance *ec2.Instance) bool {
   847  	return bd.DeviceName != nil &&
   848  		instance.RootDeviceName != nil &&
   849  		*bd.DeviceName == *instance.RootDeviceName
   850  }
   851  
   852  func fetchRootDeviceName(ami string, conn *ec2.EC2) (*string, error) {
   853  	if ami == "" {
   854  		return nil, errors.New("Cannot fetch root device name for blank AMI ID.")
   855  	}
   856  
   857  	log.Printf("[DEBUG] Describing AMI %q to get root block device name", ami)
   858  	res, err := conn.DescribeImages(&ec2.DescribeImagesInput{
   859  		ImageIds: []*string{aws.String(ami)},
   860  	})
   861  	if err != nil {
   862  		return nil, err
   863  	}
   864  
   865  	// For a bad image, we just return nil so we don't block a refresh
   866  	if len(res.Images) == 0 {
   867  		return nil, nil
   868  	}
   869  
   870  	image := res.Images[0]
   871  	rootDeviceName := image.RootDeviceName
   872  
   873  	// Instance store backed AMIs do not provide a root device name.
   874  	if *image.RootDeviceType == ec2.DeviceTypeInstanceStore {
   875  		return nil, nil
   876  	}
   877  
   878  	// Some AMIs have a RootDeviceName like "/dev/sda1" that does not appear as a
   879  	// DeviceName in the BlockDeviceMapping list (which will instead have
   880  	// something like "/dev/sda")
   881  	//
   882  	// While this seems like it breaks an invariant of AMIs, it ends up working
   883  	// on the AWS side, and AMIs like this are common enough that we need to
   884  	// special case it so Terraform does the right thing.
   885  	//
   886  	// Our heuristic is: if the RootDeviceName does not appear in the
   887  	// BlockDeviceMapping, assume that the DeviceName of the first
   888  	// BlockDeviceMapping entry serves as the root device.
   889  	rootDeviceNameInMapping := false
   890  	for _, bdm := range image.BlockDeviceMappings {
   891  		if bdm.DeviceName == image.RootDeviceName {
   892  			rootDeviceNameInMapping = true
   893  		}
   894  	}
   895  
   896  	if !rootDeviceNameInMapping && len(image.BlockDeviceMappings) > 0 {
   897  		rootDeviceName = image.BlockDeviceMappings[0].DeviceName
   898  	}
   899  
   900  	if rootDeviceName == nil {
   901  		return nil, fmt.Errorf("[WARN] Error finding Root Device Name for AMI (%s)", ami)
   902  	}
   903  
   904  	return rootDeviceName, nil
   905  }
   906  
   907  func readBlockDeviceMappingsFromConfig(
   908  	d *schema.ResourceData, conn *ec2.EC2) ([]*ec2.BlockDeviceMapping, error) {
   909  	blockDevices := make([]*ec2.BlockDeviceMapping, 0)
   910  
   911  	if v, ok := d.GetOk("ebs_block_device"); ok {
   912  		vL := v.(*schema.Set).List()
   913  		for _, v := range vL {
   914  			bd := v.(map[string]interface{})
   915  			ebs := &ec2.EbsBlockDevice{
   916  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
   917  			}
   918  
   919  			if v, ok := bd["snapshot_id"].(string); ok && v != "" {
   920  				ebs.SnapshotId = aws.String(v)
   921  			}
   922  
   923  			if v, ok := bd["encrypted"].(bool); ok && v {
   924  				ebs.Encrypted = aws.Bool(v)
   925  			}
   926  
   927  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
   928  				ebs.VolumeSize = aws.Int64(int64(v))
   929  			}
   930  
   931  			if v, ok := bd["volume_type"].(string); ok && v != "" {
   932  				ebs.VolumeType = aws.String(v)
   933  			}
   934  
   935  			if v, ok := bd["iops"].(int); ok && v > 0 {
   936  				ebs.Iops = aws.Int64(int64(v))
   937  			}
   938  
   939  			blockDevices = append(blockDevices, &ec2.BlockDeviceMapping{
   940  				DeviceName: aws.String(bd["device_name"].(string)),
   941  				Ebs:        ebs,
   942  			})
   943  		}
   944  	}
   945  
   946  	if v, ok := d.GetOk("ephemeral_block_device"); ok {
   947  		vL := v.(*schema.Set).List()
   948  		for _, v := range vL {
   949  			bd := v.(map[string]interface{})
   950  			bdm := &ec2.BlockDeviceMapping{
   951  				DeviceName:  aws.String(bd["device_name"].(string)),
   952  				VirtualName: aws.String(bd["virtual_name"].(string)),
   953  			}
   954  			if v, ok := bd["no_device"].(bool); ok && v {
   955  				bdm.NoDevice = aws.String("")
   956  				// When NoDevice is true, just ignore VirtualName since it's not needed
   957  				bdm.VirtualName = nil
   958  			}
   959  
   960  			if bdm.NoDevice == nil && aws.StringValue(bdm.VirtualName) == "" {
   961  				return nil, errors.New("virtual_name cannot be empty when no_device is false or undefined.")
   962  			}
   963  
   964  			blockDevices = append(blockDevices, bdm)
   965  		}
   966  	}
   967  
   968  	if v, ok := d.GetOk("root_block_device"); ok {
   969  		vL := v.([]interface{})
   970  		if len(vL) > 1 {
   971  			return nil, errors.New("Cannot specify more than one root_block_device.")
   972  		}
   973  		for _, v := range vL {
   974  			bd := v.(map[string]interface{})
   975  			ebs := &ec2.EbsBlockDevice{
   976  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
   977  			}
   978  
   979  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
   980  				ebs.VolumeSize = aws.Int64(int64(v))
   981  			}
   982  
   983  			if v, ok := bd["volume_type"].(string); ok && v != "" {
   984  				ebs.VolumeType = aws.String(v)
   985  			}
   986  
   987  			if v, ok := bd["iops"].(int); ok && v > 0 && *ebs.VolumeType == "io1" {
   988  				// Only set the iops attribute if the volume type is io1. Setting otherwise
   989  				// can trigger a refresh/plan loop based on the computed value that is given
   990  				// from AWS, and prevent us from specifying 0 as a valid iops.
   991  				//   See https://github.com/hashicorp/terraform/pull/4146
   992  				//   See https://github.com/hashicorp/terraform/issues/7765
   993  				ebs.Iops = aws.Int64(int64(v))
   994  			} else if v, ok := bd["iops"].(int); ok && v > 0 && *ebs.VolumeType != "io1" {
   995  				// Message user about incompatibility
   996  				log.Print("[WARN] IOPs is only valid for storate type io1 for EBS Volumes")
   997  			}
   998  
   999  			if dn, err := fetchRootDeviceName(d.Get("ami").(string), conn); err == nil {
  1000  				if dn == nil {
  1001  					return nil, fmt.Errorf(
  1002  						"Expected 1 AMI for ID: %s, got none",
  1003  						d.Get("ami").(string))
  1004  				}
  1005  
  1006  				blockDevices = append(blockDevices, &ec2.BlockDeviceMapping{
  1007  					DeviceName: dn,
  1008  					Ebs:        ebs,
  1009  				})
  1010  			} else {
  1011  				return nil, err
  1012  			}
  1013  		}
  1014  	}
  1015  
  1016  	return blockDevices, nil
  1017  }
  1018  
  1019  // Determine whether we're referring to security groups with
  1020  // IDs or names. We use a heuristic to figure this out. By default,
  1021  // we use IDs if we're in a VPC. However, if we previously had an
  1022  // all-name list of security groups, we use names. Or, if we had any
  1023  // IDs, we use IDs.
  1024  func readSecurityGroups(d *schema.ResourceData, instance *ec2.Instance) error {
  1025  	useID := instance.SubnetId != nil && *instance.SubnetId != ""
  1026  	if v := d.Get("security_groups"); v != nil {
  1027  		match := useID
  1028  		sgs := v.(*schema.Set).List()
  1029  		if len(sgs) > 0 {
  1030  			match = false
  1031  			for _, v := range v.(*schema.Set).List() {
  1032  				if strings.HasPrefix(v.(string), "sg-") {
  1033  					match = true
  1034  					break
  1035  				}
  1036  			}
  1037  		}
  1038  
  1039  		useID = match
  1040  	}
  1041  
  1042  	// Build up the security groups
  1043  	sgs := make([]string, 0, len(instance.SecurityGroups))
  1044  	if useID {
  1045  		for _, sg := range instance.SecurityGroups {
  1046  			sgs = append(sgs, *sg.GroupId)
  1047  		}
  1048  		log.Printf("[DEBUG] Setting Security Group IDs: %#v", sgs)
  1049  		if err := d.Set("vpc_security_group_ids", sgs); err != nil {
  1050  			return err
  1051  		}
  1052  		if err := d.Set("security_groups", []string{}); err != nil {
  1053  			return err
  1054  		}
  1055  	} else {
  1056  		for _, sg := range instance.SecurityGroups {
  1057  			sgs = append(sgs, *sg.GroupName)
  1058  		}
  1059  		log.Printf("[DEBUG] Setting Security Group Names: %#v", sgs)
  1060  		if err := d.Set("security_groups", sgs); err != nil {
  1061  			return err
  1062  		}
  1063  		if err := d.Set("vpc_security_group_ids", []string{}); err != nil {
  1064  			return err
  1065  		}
  1066  	}
  1067  	return nil
  1068  }
  1069  
  1070  type awsInstanceOpts struct {
  1071  	BlockDeviceMappings               []*ec2.BlockDeviceMapping
  1072  	DisableAPITermination             *bool
  1073  	EBSOptimized                      *bool
  1074  	Monitoring                        *ec2.RunInstancesMonitoringEnabled
  1075  	IAMInstanceProfile                *ec2.IamInstanceProfileSpecification
  1076  	ImageID                           *string
  1077  	InstanceInitiatedShutdownBehavior *string
  1078  	InstanceType                      *string
  1079  	KeyName                           *string
  1080  	NetworkInterfaces                 []*ec2.InstanceNetworkInterfaceSpecification
  1081  	Placement                         *ec2.Placement
  1082  	PrivateIPAddress                  *string
  1083  	SecurityGroupIDs                  []*string
  1084  	SecurityGroups                    []*string
  1085  	SpotPlacement                     *ec2.SpotPlacement
  1086  	SubnetID                          *string
  1087  	UserData64                        *string
  1088  }
  1089  
  1090  func buildAwsInstanceOpts(
  1091  	d *schema.ResourceData, meta interface{}) (*awsInstanceOpts, error) {
  1092  	conn := meta.(*AWSClient).ec2conn
  1093  
  1094  	opts := &awsInstanceOpts{
  1095  		DisableAPITermination: aws.Bool(d.Get("disable_api_termination").(bool)),
  1096  		EBSOptimized:          aws.Bool(d.Get("ebs_optimized").(bool)),
  1097  		ImageID:               aws.String(d.Get("ami").(string)),
  1098  		InstanceType:          aws.String(d.Get("instance_type").(string)),
  1099  	}
  1100  
  1101  	if v := d.Get("instance_initiated_shutdown_behavior").(string); v != "" {
  1102  		opts.InstanceInitiatedShutdownBehavior = aws.String(v)
  1103  	}
  1104  
  1105  	opts.Monitoring = &ec2.RunInstancesMonitoringEnabled{
  1106  		Enabled: aws.Bool(d.Get("monitoring").(bool)),
  1107  	}
  1108  
  1109  	opts.IAMInstanceProfile = &ec2.IamInstanceProfileSpecification{
  1110  		Name: aws.String(d.Get("iam_instance_profile").(string)),
  1111  	}
  1112  
  1113  	user_data := d.Get("user_data").(string)
  1114  
  1115  	opts.UserData64 = aws.String(base64Encode([]byte(user_data)))
  1116  
  1117  	// check for non-default Subnet, and cast it to a String
  1118  	subnet, hasSubnet := d.GetOk("subnet_id")
  1119  	subnetID := subnet.(string)
  1120  
  1121  	// Placement is used for aws_instance; SpotPlacement is used for
  1122  	// aws_spot_instance_request. They represent the same data. :-|
  1123  	opts.Placement = &ec2.Placement{
  1124  		AvailabilityZone: aws.String(d.Get("availability_zone").(string)),
  1125  		GroupName:        aws.String(d.Get("placement_group").(string)),
  1126  	}
  1127  
  1128  	opts.SpotPlacement = &ec2.SpotPlacement{
  1129  		AvailabilityZone: aws.String(d.Get("availability_zone").(string)),
  1130  		GroupName:        aws.String(d.Get("placement_group").(string)),
  1131  	}
  1132  
  1133  	if v := d.Get("tenancy").(string); v != "" {
  1134  		opts.Placement.Tenancy = aws.String(v)
  1135  	}
  1136  
  1137  	associatePublicIPAddress := d.Get("associate_public_ip_address").(bool)
  1138  
  1139  	var groups []*string
  1140  	if v := d.Get("security_groups"); v != nil {
  1141  		// Security group names.
  1142  		// For a nondefault VPC, you must use security group IDs instead.
  1143  		// See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RunInstances.html
  1144  		sgs := v.(*schema.Set).List()
  1145  		if len(sgs) > 0 && hasSubnet {
  1146  			log.Print("[WARN] Deprecated. Attempting to use 'security_groups' within a VPC instance. Use 'vpc_security_group_ids' instead.")
  1147  		}
  1148  		for _, v := range sgs {
  1149  			str := v.(string)
  1150  			groups = append(groups, aws.String(str))
  1151  		}
  1152  	}
  1153  
  1154  	if hasSubnet && associatePublicIPAddress {
  1155  		// If we have a non-default VPC / Subnet specified, we can flag
  1156  		// AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided.
  1157  		// You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise
  1158  		// you get: Network interfaces and an instance-level subnet ID may not be specified on the same request
  1159  		// You also need to attach Security Groups to the NetworkInterface instead of the instance,
  1160  		// to avoid: Network interfaces and an instance-level security groups may not be specified on
  1161  		// the same request
  1162  		ni := &ec2.InstanceNetworkInterfaceSpecification{
  1163  			AssociatePublicIpAddress: aws.Bool(associatePublicIPAddress),
  1164  			DeviceIndex:              aws.Int64(int64(0)),
  1165  			SubnetId:                 aws.String(subnetID),
  1166  			Groups:                   groups,
  1167  		}
  1168  
  1169  		if v, ok := d.GetOk("private_ip"); ok {
  1170  			ni.PrivateIpAddress = aws.String(v.(string))
  1171  		}
  1172  
  1173  		if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
  1174  			for _, v := range v.List() {
  1175  				ni.Groups = append(ni.Groups, aws.String(v.(string)))
  1176  			}
  1177  		}
  1178  
  1179  		opts.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{ni}
  1180  	} else {
  1181  		if subnetID != "" {
  1182  			opts.SubnetID = aws.String(subnetID)
  1183  		}
  1184  
  1185  		if v, ok := d.GetOk("private_ip"); ok {
  1186  			opts.PrivateIPAddress = aws.String(v.(string))
  1187  		}
  1188  		if opts.SubnetID != nil &&
  1189  			*opts.SubnetID != "" {
  1190  			opts.SecurityGroupIDs = groups
  1191  		} else {
  1192  			opts.SecurityGroups = groups
  1193  		}
  1194  
  1195  		if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
  1196  			for _, v := range v.List() {
  1197  				opts.SecurityGroupIDs = append(opts.SecurityGroupIDs, aws.String(v.(string)))
  1198  			}
  1199  		}
  1200  	}
  1201  
  1202  	if v, ok := d.GetOk("key_name"); ok {
  1203  		opts.KeyName = aws.String(v.(string))
  1204  	}
  1205  
  1206  	blockDevices, err := readBlockDeviceMappingsFromConfig(d, conn)
  1207  	if err != nil {
  1208  		return nil, err
  1209  	}
  1210  	if len(blockDevices) > 0 {
  1211  		opts.BlockDeviceMappings = blockDevices
  1212  	}
  1213  
  1214  	return opts, nil
  1215  }
  1216  
  1217  func awsTerminateInstance(conn *ec2.EC2, id string) error {
  1218  	log.Printf("[INFO] Terminating instance: %s", id)
  1219  	req := &ec2.TerminateInstancesInput{
  1220  		InstanceIds: []*string{aws.String(id)},
  1221  	}
  1222  	if _, err := conn.TerminateInstances(req); err != nil {
  1223  		return fmt.Errorf("Error terminating instance: %s", err)
  1224  	}
  1225  
  1226  	log.Printf("[DEBUG] Waiting for instance (%s) to become terminated", id)
  1227  
  1228  	stateConf := &resource.StateChangeConf{
  1229  		Pending:    []string{"pending", "running", "shutting-down", "stopped", "stopping"},
  1230  		Target:     []string{"terminated"},
  1231  		Refresh:    InstanceStateRefreshFunc(conn, id),
  1232  		Timeout:    10 * time.Minute,
  1233  		Delay:      10 * time.Second,
  1234  		MinTimeout: 3 * time.Second,
  1235  	}
  1236  
  1237  	_, err := stateConf.WaitForState()
  1238  	if err != nil {
  1239  		return fmt.Errorf(
  1240  			"Error waiting for instance (%s) to terminate: %s", id, err)
  1241  	}
  1242  
  1243  	return nil
  1244  }
  1245  
  1246  func iamInstanceProfileArnToName(ip *ec2.IamInstanceProfile) string {
  1247  	if ip == nil || ip.Arn == nil {
  1248  		return ""
  1249  	}
  1250  	parts := strings.Split(*ip.Arn, "/")
  1251  	return parts[len(parts)-1]
  1252  }
  1253  
  1254  func userDataHashSum(user_data string) string {
  1255  	// Check whether the user_data is not Base64 encoded.
  1256  	// Always calculate hash of base64 decoded value since we
  1257  	// check against double-encoding when setting it
  1258  	v, base64DecodeError := base64.StdEncoding.DecodeString(user_data)
  1259  	if base64DecodeError != nil {
  1260  		v = []byte(user_data)
  1261  	}
  1262  
  1263  	hash := sha1.Sum(v)
  1264  	return hex.EncodeToString(hash[:])
  1265  }