github.com/candidpartners/terraform@v0.9.5-0.20171005231213-29f5f88820f6/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  			// TODO: Deprecate me v0.10.0
   132  			"network_interface_id": {
   133  				Type:       schema.TypeString,
   134  				Computed:   true,
   135  				Deprecated: "Please use `primary_network_interface_id` instead",
   136  			},
   137  
   138  			"primary_network_interface_id": {
   139  				Type:     schema.TypeString,
   140  				Computed: true,
   141  			},
   142  
   143  			"network_interface": {
   144  				ConflictsWith: []string{"associate_public_ip_address", "subnet_id", "private_ip", "vpc_security_group_ids", "security_groups", "ipv6_addresses", "ipv6_address_count", "source_dest_check"},
   145  				Type:          schema.TypeSet,
   146  				Optional:      true,
   147  				Computed:      true,
   148  				Elem: &schema.Resource{
   149  					Schema: map[string]*schema.Schema{
   150  						"delete_on_termination": {
   151  							Type:     schema.TypeBool,
   152  							Default:  false,
   153  							Optional: true,
   154  							ForceNew: true,
   155  						},
   156  						"network_interface_id": {
   157  							Type:     schema.TypeString,
   158  							Required: true,
   159  							ForceNew: true,
   160  						},
   161  						"device_index": {
   162  							Type:     schema.TypeInt,
   163  							Required: true,
   164  							ForceNew: true,
   165  						},
   166  					},
   167  				},
   168  			},
   169  
   170  			"public_ip": {
   171  				Type:     schema.TypeString,
   172  				Computed: true,
   173  			},
   174  
   175  			"instance_state": {
   176  				Type:     schema.TypeString,
   177  				Computed: true,
   178  			},
   179  
   180  			"private_dns": {
   181  				Type:     schema.TypeString,
   182  				Computed: true,
   183  			},
   184  
   185  			"ebs_optimized": {
   186  				Type:     schema.TypeBool,
   187  				Optional: true,
   188  				ForceNew: true,
   189  			},
   190  
   191  			"disable_api_termination": {
   192  				Type:     schema.TypeBool,
   193  				Optional: true,
   194  			},
   195  
   196  			"instance_initiated_shutdown_behavior": {
   197  				Type:     schema.TypeString,
   198  				Optional: true,
   199  			},
   200  
   201  			"monitoring": {
   202  				Type:     schema.TypeBool,
   203  				Optional: true,
   204  			},
   205  
   206  			"iam_instance_profile": {
   207  				Type:     schema.TypeString,
   208  				Optional: true,
   209  			},
   210  
   211  			"ipv6_address_count": {
   212  				Type:     schema.TypeInt,
   213  				Optional: true,
   214  				ForceNew: true,
   215  			},
   216  
   217  			"ipv6_addresses": {
   218  				Type:     schema.TypeList,
   219  				Optional: true,
   220  				Computed: true,
   221  				ForceNew: true,
   222  				Elem: &schema.Schema{
   223  					Type: schema.TypeString,
   224  				},
   225  				ConflictsWith: []string{"ipv6_address_count"},
   226  			},
   227  
   228  			"tenancy": {
   229  				Type:     schema.TypeString,
   230  				Optional: true,
   231  				Computed: true,
   232  				ForceNew: true,
   233  			},
   234  
   235  			"tags": tagsSchema(),
   236  
   237  			"volume_tags": tagsSchemaComputed(),
   238  
   239  			"block_device": {
   240  				Type:     schema.TypeMap,
   241  				Optional: true,
   242  				Removed:  "Split out into three sub-types; see Changelog and Docs",
   243  			},
   244  
   245  			"ebs_block_device": {
   246  				Type:     schema.TypeSet,
   247  				Optional: true,
   248  				Computed: true,
   249  				Elem: &schema.Resource{
   250  					Schema: map[string]*schema.Schema{
   251  						"delete_on_termination": {
   252  							Type:     schema.TypeBool,
   253  							Optional: true,
   254  							Default:  true,
   255  							ForceNew: true,
   256  						},
   257  
   258  						"device_name": {
   259  							Type:     schema.TypeString,
   260  							Required: true,
   261  							ForceNew: true,
   262  						},
   263  
   264  						"encrypted": {
   265  							Type:     schema.TypeBool,
   266  							Optional: true,
   267  							Computed: true,
   268  							ForceNew: true,
   269  						},
   270  
   271  						"iops": {
   272  							Type:     schema.TypeInt,
   273  							Optional: true,
   274  							Computed: true,
   275  							ForceNew: true,
   276  						},
   277  
   278  						"snapshot_id": {
   279  							Type:     schema.TypeString,
   280  							Optional: true,
   281  							Computed: true,
   282  							ForceNew: true,
   283  						},
   284  
   285  						"volume_size": {
   286  							Type:     schema.TypeInt,
   287  							Optional: true,
   288  							Computed: true,
   289  							ForceNew: true,
   290  						},
   291  
   292  						"volume_type": {
   293  							Type:     schema.TypeString,
   294  							Optional: true,
   295  							Computed: true,
   296  							ForceNew: true,
   297  						},
   298  					},
   299  				},
   300  				Set: func(v interface{}) int {
   301  					var buf bytes.Buffer
   302  					m := v.(map[string]interface{})
   303  					buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
   304  					buf.WriteString(fmt.Sprintf("%s-", m["snapshot_id"].(string)))
   305  					return hashcode.String(buf.String())
   306  				},
   307  			},
   308  
   309  			"ephemeral_block_device": {
   310  				Type:     schema.TypeSet,
   311  				Optional: true,
   312  				Computed: true,
   313  				ForceNew: true,
   314  				Elem: &schema.Resource{
   315  					Schema: map[string]*schema.Schema{
   316  						"device_name": {
   317  							Type:     schema.TypeString,
   318  							Required: true,
   319  						},
   320  
   321  						"virtual_name": {
   322  							Type:     schema.TypeString,
   323  							Optional: true,
   324  						},
   325  
   326  						"no_device": {
   327  							Type:     schema.TypeBool,
   328  							Optional: true,
   329  						},
   330  					},
   331  				},
   332  				Set: func(v interface{}) int {
   333  					var buf bytes.Buffer
   334  					m := v.(map[string]interface{})
   335  					buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
   336  					buf.WriteString(fmt.Sprintf("%s-", m["virtual_name"].(string)))
   337  					if v, ok := m["no_device"].(bool); ok && v {
   338  						buf.WriteString(fmt.Sprintf("%t-", v))
   339  					}
   340  					return hashcode.String(buf.String())
   341  				},
   342  			},
   343  
   344  			"root_block_device": {
   345  				Type:     schema.TypeList,
   346  				Optional: true,
   347  				Computed: true,
   348  				MaxItems: 1,
   349  				Elem: &schema.Resource{
   350  					// "You can only modify the volume size, volume type, and Delete on
   351  					// Termination flag on the block device mapping entry for the root
   352  					// device volume." - bit.ly/ec2bdmap
   353  					Schema: map[string]*schema.Schema{
   354  						"delete_on_termination": {
   355  							Type:     schema.TypeBool,
   356  							Optional: true,
   357  							Default:  true,
   358  							ForceNew: true,
   359  						},
   360  
   361  						"iops": {
   362  							Type:     schema.TypeInt,
   363  							Optional: true,
   364  							Computed: true,
   365  							ForceNew: true,
   366  						},
   367  
   368  						"volume_size": {
   369  							Type:     schema.TypeInt,
   370  							Optional: true,
   371  							Computed: true,
   372  							ForceNew: true,
   373  						},
   374  
   375  						"volume_type": {
   376  							Type:     schema.TypeString,
   377  							Optional: true,
   378  							Computed: true,
   379  							ForceNew: true,
   380  						},
   381  					},
   382  				},
   383  			},
   384  		},
   385  	}
   386  }
   387  
   388  func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   389  	conn := meta.(*AWSClient).ec2conn
   390  
   391  	instanceOpts, err := buildAwsInstanceOpts(d, meta)
   392  	if err != nil {
   393  		return err
   394  	}
   395  
   396  	// Build the creation struct
   397  	runOpts := &ec2.RunInstancesInput{
   398  		BlockDeviceMappings:   instanceOpts.BlockDeviceMappings,
   399  		DisableApiTermination: instanceOpts.DisableAPITermination,
   400  		EbsOptimized:          instanceOpts.EBSOptimized,
   401  		Monitoring:            instanceOpts.Monitoring,
   402  		IamInstanceProfile:    instanceOpts.IAMInstanceProfile,
   403  		ImageId:               instanceOpts.ImageID,
   404  		InstanceInitiatedShutdownBehavior: instanceOpts.InstanceInitiatedShutdownBehavior,
   405  		InstanceType:                      instanceOpts.InstanceType,
   406  		KeyName:                           instanceOpts.KeyName,
   407  		MaxCount:                          aws.Int64(int64(1)),
   408  		MinCount:                          aws.Int64(int64(1)),
   409  		NetworkInterfaces:                 instanceOpts.NetworkInterfaces,
   410  		Placement:                         instanceOpts.Placement,
   411  		PrivateIpAddress:                  instanceOpts.PrivateIPAddress,
   412  		SecurityGroupIds:                  instanceOpts.SecurityGroupIDs,
   413  		SecurityGroups:                    instanceOpts.SecurityGroups,
   414  		SubnetId:                          instanceOpts.SubnetID,
   415  		UserData:                          instanceOpts.UserData64,
   416  	}
   417  
   418  	if v, ok := d.GetOk("ipv6_address_count"); ok {
   419  		runOpts.Ipv6AddressCount = aws.Int64(int64(v.(int)))
   420  	}
   421  
   422  	if v, ok := d.GetOk("ipv6_addresses"); ok {
   423  		ipv6Addresses := make([]*ec2.InstanceIpv6Address, len(v.([]interface{})))
   424  		for _, address := range v.([]interface{}) {
   425  			ipv6Address := &ec2.InstanceIpv6Address{
   426  				Ipv6Address: aws.String(address.(string)),
   427  			}
   428  
   429  			ipv6Addresses = append(ipv6Addresses, ipv6Address)
   430  		}
   431  
   432  		runOpts.Ipv6Addresses = ipv6Addresses
   433  	}
   434  
   435  	restricted := meta.(*AWSClient).IsGovCloud() || meta.(*AWSClient).IsChinaCloud()
   436  	if !restricted {
   437  		tagsSpec := make([]*ec2.TagSpecification, 0)
   438  
   439  		if v, ok := d.GetOk("tags"); ok {
   440  			tags := tagsFromMap(v.(map[string]interface{}))
   441  
   442  			spec := &ec2.TagSpecification{
   443  				ResourceType: aws.String("instance"),
   444  				Tags:         tags,
   445  			}
   446  
   447  			tagsSpec = append(tagsSpec, spec)
   448  		}
   449  
   450  		if v, ok := d.GetOk("volume_tags"); ok {
   451  			tags := tagsFromMap(v.(map[string]interface{}))
   452  
   453  			spec := &ec2.TagSpecification{
   454  				ResourceType: aws.String("volume"),
   455  				Tags:         tags,
   456  			}
   457  
   458  			tagsSpec = append(tagsSpec, spec)
   459  		}
   460  
   461  		if len(tagsSpec) > 0 {
   462  			runOpts.TagSpecifications = tagsSpec
   463  		}
   464  	}
   465  
   466  	// Create the instance
   467  	log.Printf("[DEBUG] Run configuration: %s", runOpts)
   468  
   469  	var runResp *ec2.Reservation
   470  	err = resource.Retry(15*time.Second, func() *resource.RetryError {
   471  		var err error
   472  		runResp, err = conn.RunInstances(runOpts)
   473  		// IAM instance profiles can take ~10 seconds to propagate in AWS:
   474  		// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
   475  		if isAWSErr(err, "InvalidParameterValue", "Invalid IAM Instance Profile") {
   476  			log.Print("[DEBUG] Invalid IAM Instance Profile referenced, retrying...")
   477  			return resource.RetryableError(err)
   478  		}
   479  		// IAM roles can also take time to propagate in AWS:
   480  		if isAWSErr(err, "InvalidParameterValue", " has no associated IAM Roles") {
   481  			log.Print("[DEBUG] IAM Instance Profile appears to have no IAM roles, retrying...")
   482  			return resource.RetryableError(err)
   483  		}
   484  		return resource.NonRetryableError(err)
   485  	})
   486  	// Warn if the AWS Error involves group ids, to help identify situation
   487  	// where a user uses group ids in security_groups for the Default VPC.
   488  	//   See https://github.com/hashicorp/terraform/issues/3798
   489  	if isAWSErr(err, "InvalidParameterValue", "groupId is invalid") {
   490  		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())
   491  	}
   492  	if err != nil {
   493  		return fmt.Errorf("Error launching source instance: %s", err)
   494  	}
   495  	if runResp == nil || len(runResp.Instances) == 0 {
   496  		return errors.New("Error launching source instance: no instances returned in response")
   497  	}
   498  
   499  	instance := runResp.Instances[0]
   500  	log.Printf("[INFO] Instance ID: %s", *instance.InstanceId)
   501  
   502  	// Store the resulting ID so we can look this up later
   503  	d.SetId(*instance.InstanceId)
   504  
   505  	// Wait for the instance to become running so we can get some attributes
   506  	// that aren't available until later.
   507  	log.Printf(
   508  		"[DEBUG] Waiting for instance (%s) to become running",
   509  		*instance.InstanceId)
   510  
   511  	stateConf := &resource.StateChangeConf{
   512  		Pending:    []string{"pending"},
   513  		Target:     []string{"running"},
   514  		Refresh:    InstanceStateRefreshFunc(conn, *instance.InstanceId),
   515  		Timeout:    10 * time.Minute,
   516  		Delay:      10 * time.Second,
   517  		MinTimeout: 3 * time.Second,
   518  	}
   519  
   520  	instanceRaw, err := stateConf.WaitForState()
   521  	if err != nil {
   522  		return fmt.Errorf(
   523  			"Error waiting for instance (%s) to become ready: %s",
   524  			*instance.InstanceId, err)
   525  	}
   526  
   527  	instance = instanceRaw.(*ec2.Instance)
   528  
   529  	// Initialize the connection info
   530  	if instance.PublicIpAddress != nil {
   531  		d.SetConnInfo(map[string]string{
   532  			"type": "ssh",
   533  			"host": *instance.PublicIpAddress,
   534  		})
   535  	} else if instance.PrivateIpAddress != nil {
   536  		d.SetConnInfo(map[string]string{
   537  			"type": "ssh",
   538  			"host": *instance.PrivateIpAddress,
   539  		})
   540  	}
   541  
   542  	// Update if we need to
   543  	return resourceAwsInstanceUpdate(d, meta)
   544  }
   545  
   546  func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
   547  	conn := meta.(*AWSClient).ec2conn
   548  
   549  	resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{
   550  		InstanceIds: []*string{aws.String(d.Id())},
   551  	})
   552  	if err != nil {
   553  		// If the instance was not found, return nil so that we can show
   554  		// that the instance is gone.
   555  		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" {
   556  			d.SetId("")
   557  			return nil
   558  		}
   559  
   560  		// Some other error, report it
   561  		return err
   562  	}
   563  
   564  	// If nothing was found, then return no state
   565  	if len(resp.Reservations) == 0 {
   566  		d.SetId("")
   567  		return nil
   568  	}
   569  
   570  	instance := resp.Reservations[0].Instances[0]
   571  
   572  	if instance.State != nil {
   573  		// If the instance is terminated, then it is gone
   574  		if *instance.State.Name == "terminated" {
   575  			d.SetId("")
   576  			return nil
   577  		}
   578  
   579  		d.Set("instance_state", instance.State.Name)
   580  	}
   581  
   582  	if instance.Placement != nil {
   583  		d.Set("availability_zone", instance.Placement.AvailabilityZone)
   584  	}
   585  	if instance.Placement.Tenancy != nil {
   586  		d.Set("tenancy", instance.Placement.Tenancy)
   587  	}
   588  
   589  	d.Set("ami", instance.ImageId)
   590  	d.Set("instance_type", instance.InstanceType)
   591  	d.Set("key_name", instance.KeyName)
   592  	d.Set("public_dns", instance.PublicDnsName)
   593  	d.Set("public_ip", instance.PublicIpAddress)
   594  	d.Set("private_dns", instance.PrivateDnsName)
   595  	d.Set("private_ip", instance.PrivateIpAddress)
   596  	d.Set("iam_instance_profile", iamInstanceProfileArnToName(instance.IamInstanceProfile))
   597  
   598  	// Set configured Network Interface Device Index Slice
   599  	// We only want to read, and populate state for the configured network_interface attachments. Otherwise, other
   600  	// resources have the potential to attach network interfaces to the instance, and cause a perpetual create/destroy
   601  	// diff. We should only read on changes configured for this specific resource because of this.
   602  	var configuredDeviceIndexes []int
   603  	if v, ok := d.GetOk("network_interface"); ok {
   604  		vL := v.(*schema.Set).List()
   605  		for _, vi := range vL {
   606  			mVi := vi.(map[string]interface{})
   607  			configuredDeviceIndexes = append(configuredDeviceIndexes, mVi["device_index"].(int))
   608  		}
   609  	}
   610  
   611  	var ipv6Addresses []string
   612  	if len(instance.NetworkInterfaces) > 0 {
   613  		var primaryNetworkInterface ec2.InstanceNetworkInterface
   614  		var networkInterfaces []map[string]interface{}
   615  		for _, iNi := range instance.NetworkInterfaces {
   616  			ni := make(map[string]interface{})
   617  			if *iNi.Attachment.DeviceIndex == 0 {
   618  				primaryNetworkInterface = *iNi
   619  			}
   620  			// If the attached network device is inside our configuration, refresh state with values found.
   621  			// Otherwise, assume the network device was attached via an outside resource.
   622  			for _, index := range configuredDeviceIndexes {
   623  				if index == int(*iNi.Attachment.DeviceIndex) {
   624  					ni["device_index"] = *iNi.Attachment.DeviceIndex
   625  					ni["network_interface_id"] = *iNi.NetworkInterfaceId
   626  					ni["delete_on_termination"] = *iNi.Attachment.DeleteOnTermination
   627  				}
   628  			}
   629  			// Don't add empty network interfaces to schema
   630  			if len(ni) == 0 {
   631  				continue
   632  			}
   633  			networkInterfaces = append(networkInterfaces, ni)
   634  		}
   635  		if err := d.Set("network_interface", networkInterfaces); err != nil {
   636  			return fmt.Errorf("Error setting network_interfaces: %v", err)
   637  		}
   638  
   639  		// Set primary network interface details
   640  		d.Set("subnet_id", primaryNetworkInterface.SubnetId)
   641  		d.Set("network_interface_id", primaryNetworkInterface.NetworkInterfaceId) // TODO: Deprecate me v0.10.0
   642  		d.Set("primary_network_interface_id", primaryNetworkInterface.NetworkInterfaceId)
   643  		d.Set("associate_public_ip_address", primaryNetworkInterface.Association != nil)
   644  		d.Set("ipv6_address_count", len(primaryNetworkInterface.Ipv6Addresses))
   645  
   646  		for _, address := range primaryNetworkInterface.Ipv6Addresses {
   647  			ipv6Addresses = append(ipv6Addresses, *address.Ipv6Address)
   648  		}
   649  
   650  	} else {
   651  		d.Set("subnet_id", instance.SubnetId)
   652  		d.Set("network_interface_id", "") // TODO: Deprecate me v0.10.0
   653  		d.Set("primary_network_interface_id", "")
   654  	}
   655  
   656  	if err := d.Set("ipv6_addresses", ipv6Addresses); err != nil {
   657  		log.Printf("[WARN] Error setting ipv6_addresses for AWS Instance (%s): %s", d.Id(), err)
   658  	}
   659  
   660  	d.Set("ebs_optimized", instance.EbsOptimized)
   661  	if instance.SubnetId != nil && *instance.SubnetId != "" {
   662  		d.Set("source_dest_check", instance.SourceDestCheck)
   663  	}
   664  
   665  	if instance.Monitoring != nil && instance.Monitoring.State != nil {
   666  		monitoringState := *instance.Monitoring.State
   667  		d.Set("monitoring", monitoringState == "enabled" || monitoringState == "pending")
   668  	}
   669  
   670  	d.Set("tags", tagsToMap(instance.Tags))
   671  
   672  	if err := readVolumeTags(conn, d); err != nil {
   673  		return err
   674  	}
   675  
   676  	if err := readSecurityGroups(d, instance); err != nil {
   677  		return err
   678  	}
   679  
   680  	if err := readBlockDevices(d, instance, conn); err != nil {
   681  		return err
   682  	}
   683  	if _, ok := d.GetOk("ephemeral_block_device"); !ok {
   684  		d.Set("ephemeral_block_device", []interface{}{})
   685  	}
   686  
   687  	// Instance attributes
   688  	{
   689  		attr, err := conn.DescribeInstanceAttribute(&ec2.DescribeInstanceAttributeInput{
   690  			Attribute:  aws.String("disableApiTermination"),
   691  			InstanceId: aws.String(d.Id()),
   692  		})
   693  		if err != nil {
   694  			return err
   695  		}
   696  		d.Set("disable_api_termination", attr.DisableApiTermination.Value)
   697  	}
   698  	{
   699  		attr, err := conn.DescribeInstanceAttribute(&ec2.DescribeInstanceAttributeInput{
   700  			Attribute:  aws.String(ec2.InstanceAttributeNameUserData),
   701  			InstanceId: aws.String(d.Id()),
   702  		})
   703  		if err != nil {
   704  			return err
   705  		}
   706  		if attr.UserData.Value != nil {
   707  			d.Set("user_data", userDataHashSum(*attr.UserData.Value))
   708  		}
   709  	}
   710  
   711  	return nil
   712  }
   713  
   714  func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   715  	conn := meta.(*AWSClient).ec2conn
   716  
   717  	d.Partial(true)
   718  
   719  	restricted := meta.(*AWSClient).IsGovCloud() || meta.(*AWSClient).IsChinaCloud()
   720  
   721  	if d.HasChange("tags") {
   722  		if !d.IsNewResource() || !restricted {
   723  			if err := setTags(conn, d); err != nil {
   724  				return err
   725  			} else {
   726  				d.SetPartial("tags")
   727  			}
   728  		}
   729  	}
   730  	if d.HasChange("volume_tags") {
   731  		if !d.IsNewResource() || !restricted {
   732  			if err := setVolumeTags(conn, d); err != nil {
   733  				return err
   734  			} else {
   735  				d.SetPartial("volume_tags")
   736  			}
   737  		}
   738  	}
   739  
   740  	if d.HasChange("iam_instance_profile") && !d.IsNewResource() {
   741  		request := &ec2.DescribeIamInstanceProfileAssociationsInput{
   742  			Filters: []*ec2.Filter{
   743  				{
   744  					Name:   aws.String("instance-id"),
   745  					Values: []*string{aws.String(d.Id())},
   746  				},
   747  			},
   748  		}
   749  
   750  		resp, err := conn.DescribeIamInstanceProfileAssociations(request)
   751  		if err != nil {
   752  			return err
   753  		}
   754  
   755  		// An Iam Instance Profile has been provided and is pending a change
   756  		// This means it is an association or a replacement to an association
   757  		if _, ok := d.GetOk("iam_instance_profile"); ok {
   758  			// Does not have an Iam Instance Profile associated with it, need to associate
   759  			if len(resp.IamInstanceProfileAssociations) == 0 {
   760  				_, err := conn.AssociateIamInstanceProfile(&ec2.AssociateIamInstanceProfileInput{
   761  					InstanceId: aws.String(d.Id()),
   762  					IamInstanceProfile: &ec2.IamInstanceProfileSpecification{
   763  						Name: aws.String(d.Get("iam_instance_profile").(string)),
   764  					},
   765  				})
   766  				if err != nil {
   767  					return err
   768  				}
   769  
   770  			} else {
   771  				// Has an Iam Instance Profile associated with it, need to replace the association
   772  				associationId := resp.IamInstanceProfileAssociations[0].AssociationId
   773  
   774  				_, err := conn.ReplaceIamInstanceProfileAssociation(&ec2.ReplaceIamInstanceProfileAssociationInput{
   775  					AssociationId: associationId,
   776  					IamInstanceProfile: &ec2.IamInstanceProfileSpecification{
   777  						Name: aws.String(d.Get("iam_instance_profile").(string)),
   778  					},
   779  				})
   780  				if err != nil {
   781  					return err
   782  				}
   783  			}
   784  			// An Iam Instance Profile has _not_ been provided but is pending a change. This means there is a pending removal
   785  		} else {
   786  			if len(resp.IamInstanceProfileAssociations) > 0 {
   787  				// Has an Iam Instance Profile associated with it, need to remove the association
   788  				associationId := resp.IamInstanceProfileAssociations[0].AssociationId
   789  
   790  				_, err := conn.DisassociateIamInstanceProfile(&ec2.DisassociateIamInstanceProfileInput{
   791  					AssociationId: associationId,
   792  				})
   793  				if err != nil {
   794  					return err
   795  				}
   796  			}
   797  		}
   798  	}
   799  
   800  	// SourceDestCheck can only be modified on an instance without manually specified network interfaces.
   801  	// SourceDestCheck, in that case, is configured at the network interface level
   802  	if _, ok := d.GetOk("network_interface"); !ok {
   803  		if d.HasChange("source_dest_check") || d.IsNewResource() {
   804  			// SourceDestCheck can only be set on VPC instances	// AWS will return an error of InvalidParameterCombination if we attempt
   805  			// to modify the source_dest_check of an instance in EC2 Classic
   806  			log.Printf("[INFO] Modifying `source_dest_check` on Instance %s", d.Id())
   807  			_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   808  				InstanceId: aws.String(d.Id()),
   809  				SourceDestCheck: &ec2.AttributeBooleanValue{
   810  					Value: aws.Bool(d.Get("source_dest_check").(bool)),
   811  				},
   812  			})
   813  			if err != nil {
   814  				if ec2err, ok := err.(awserr.Error); ok {
   815  					// Tolerate InvalidParameterCombination error in Classic, otherwise
   816  					// return the error
   817  					if "InvalidParameterCombination" != ec2err.Code() {
   818  						return err
   819  					}
   820  					log.Printf("[WARN] Attempted to modify SourceDestCheck on non VPC instance: %s", ec2err.Message())
   821  				}
   822  			}
   823  		}
   824  	}
   825  
   826  	if d.HasChange("vpc_security_group_ids") {
   827  		var groups []*string
   828  		if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
   829  			for _, v := range v.List() {
   830  				groups = append(groups, aws.String(v.(string)))
   831  			}
   832  		}
   833  		_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   834  			InstanceId: aws.String(d.Id()),
   835  			Groups:     groups,
   836  		})
   837  		if err != nil {
   838  			return err
   839  		}
   840  	}
   841  
   842  	if d.HasChange("instance_type") && !d.IsNewResource() {
   843  		log.Printf("[INFO] Stopping Instance %q for instance_type change", d.Id())
   844  		_, err := conn.StopInstances(&ec2.StopInstancesInput{
   845  			InstanceIds: []*string{aws.String(d.Id())},
   846  		})
   847  
   848  		stateConf := &resource.StateChangeConf{
   849  			Pending:    []string{"pending", "running", "shutting-down", "stopped", "stopping"},
   850  			Target:     []string{"stopped"},
   851  			Refresh:    InstanceStateRefreshFunc(conn, d.Id()),
   852  			Timeout:    10 * time.Minute,
   853  			Delay:      10 * time.Second,
   854  			MinTimeout: 3 * time.Second,
   855  		}
   856  
   857  		_, err = stateConf.WaitForState()
   858  		if err != nil {
   859  			return fmt.Errorf(
   860  				"Error waiting for instance (%s) to stop: %s", d.Id(), err)
   861  		}
   862  
   863  		log.Printf("[INFO] Modifying instance type %s", d.Id())
   864  		_, err = conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   865  			InstanceId: aws.String(d.Id()),
   866  			InstanceType: &ec2.AttributeValue{
   867  				Value: aws.String(d.Get("instance_type").(string)),
   868  			},
   869  		})
   870  		if err != nil {
   871  			return err
   872  		}
   873  
   874  		log.Printf("[INFO] Starting Instance %q after instance_type change", d.Id())
   875  		_, err = conn.StartInstances(&ec2.StartInstancesInput{
   876  			InstanceIds: []*string{aws.String(d.Id())},
   877  		})
   878  
   879  		stateConf = &resource.StateChangeConf{
   880  			Pending:    []string{"pending", "stopped"},
   881  			Target:     []string{"running"},
   882  			Refresh:    InstanceStateRefreshFunc(conn, d.Id()),
   883  			Timeout:    10 * time.Minute,
   884  			Delay:      10 * time.Second,
   885  			MinTimeout: 3 * time.Second,
   886  		}
   887  
   888  		_, err = stateConf.WaitForState()
   889  		if err != nil {
   890  			return fmt.Errorf(
   891  				"Error waiting for instance (%s) to become ready: %s",
   892  				d.Id(), err)
   893  		}
   894  	}
   895  
   896  	if d.HasChange("disable_api_termination") {
   897  		_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   898  			InstanceId: aws.String(d.Id()),
   899  			DisableApiTermination: &ec2.AttributeBooleanValue{
   900  				Value: aws.Bool(d.Get("disable_api_termination").(bool)),
   901  			},
   902  		})
   903  		if err != nil {
   904  			return err
   905  		}
   906  	}
   907  
   908  	if d.HasChange("instance_initiated_shutdown_behavior") {
   909  		log.Printf("[INFO] Modifying instance %s", d.Id())
   910  		_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
   911  			InstanceId: aws.String(d.Id()),
   912  			InstanceInitiatedShutdownBehavior: &ec2.AttributeValue{
   913  				Value: aws.String(d.Get("instance_initiated_shutdown_behavior").(string)),
   914  			},
   915  		})
   916  		if err != nil {
   917  			return err
   918  		}
   919  	}
   920  
   921  	if d.HasChange("monitoring") {
   922  		var mErr error
   923  		if d.Get("monitoring").(bool) {
   924  			log.Printf("[DEBUG] Enabling monitoring for Instance (%s)", d.Id())
   925  			_, mErr = conn.MonitorInstances(&ec2.MonitorInstancesInput{
   926  				InstanceIds: []*string{aws.String(d.Id())},
   927  			})
   928  		} else {
   929  			log.Printf("[DEBUG] Disabling monitoring for Instance (%s)", d.Id())
   930  			_, mErr = conn.UnmonitorInstances(&ec2.UnmonitorInstancesInput{
   931  				InstanceIds: []*string{aws.String(d.Id())},
   932  			})
   933  		}
   934  		if mErr != nil {
   935  			return fmt.Errorf("[WARN] Error updating Instance monitoring: %s", mErr)
   936  		}
   937  	}
   938  
   939  	// TODO(mitchellh): wait for the attributes we modified to
   940  	// persist the change...
   941  
   942  	d.Partial(false)
   943  
   944  	return resourceAwsInstanceRead(d, meta)
   945  }
   946  
   947  func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   948  	conn := meta.(*AWSClient).ec2conn
   949  
   950  	if err := awsTerminateInstance(conn, d.Id()); err != nil {
   951  		return err
   952  	}
   953  
   954  	d.SetId("")
   955  	return nil
   956  }
   957  
   958  // InstanceStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   959  // an EC2 instance.
   960  func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRefreshFunc {
   961  	return func() (interface{}, string, error) {
   962  		resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{
   963  			InstanceIds: []*string{aws.String(instanceID)},
   964  		})
   965  		if err != nil {
   966  			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" {
   967  				// Set this to nil as if we didn't find anything.
   968  				resp = nil
   969  			} else {
   970  				log.Printf("Error on InstanceStateRefresh: %s", err)
   971  				return nil, "", err
   972  			}
   973  		}
   974  
   975  		if resp == nil || len(resp.Reservations) == 0 || len(resp.Reservations[0].Instances) == 0 {
   976  			// Sometimes AWS just has consistency issues and doesn't see
   977  			// our instance yet. Return an empty state.
   978  			return nil, "", nil
   979  		}
   980  
   981  		i := resp.Reservations[0].Instances[0]
   982  		return i, *i.State.Name, nil
   983  	}
   984  }
   985  
   986  func readBlockDevices(d *schema.ResourceData, instance *ec2.Instance, conn *ec2.EC2) error {
   987  	ibds, err := readBlockDevicesFromInstance(instance, conn)
   988  	if err != nil {
   989  		return err
   990  	}
   991  
   992  	if err := d.Set("ebs_block_device", ibds["ebs"]); err != nil {
   993  		return err
   994  	}
   995  
   996  	// This handles the import case which needs to be defaulted to empty
   997  	if _, ok := d.GetOk("root_block_device"); !ok {
   998  		if err := d.Set("root_block_device", []interface{}{}); err != nil {
   999  			return err
  1000  		}
  1001  	}
  1002  
  1003  	if ibds["root"] != nil {
  1004  		roots := []interface{}{ibds["root"]}
  1005  		if err := d.Set("root_block_device", roots); err != nil {
  1006  			return err
  1007  		}
  1008  	}
  1009  
  1010  	return nil
  1011  }
  1012  
  1013  func readBlockDevicesFromInstance(instance *ec2.Instance, conn *ec2.EC2) (map[string]interface{}, error) {
  1014  	blockDevices := make(map[string]interface{})
  1015  	blockDevices["ebs"] = make([]map[string]interface{}, 0)
  1016  	blockDevices["root"] = nil
  1017  
  1018  	instanceBlockDevices := make(map[string]*ec2.InstanceBlockDeviceMapping)
  1019  	for _, bd := range instance.BlockDeviceMappings {
  1020  		if bd.Ebs != nil {
  1021  			instanceBlockDevices[*bd.Ebs.VolumeId] = bd
  1022  		}
  1023  	}
  1024  
  1025  	if len(instanceBlockDevices) == 0 {
  1026  		return nil, nil
  1027  	}
  1028  
  1029  	volIDs := make([]*string, 0, len(instanceBlockDevices))
  1030  	for volID := range instanceBlockDevices {
  1031  		volIDs = append(volIDs, aws.String(volID))
  1032  	}
  1033  
  1034  	// Need to call DescribeVolumes to get volume_size and volume_type for each
  1035  	// EBS block device
  1036  	volResp, err := conn.DescribeVolumes(&ec2.DescribeVolumesInput{
  1037  		VolumeIds: volIDs,
  1038  	})
  1039  	if err != nil {
  1040  		return nil, err
  1041  	}
  1042  
  1043  	for _, vol := range volResp.Volumes {
  1044  		instanceBd := instanceBlockDevices[*vol.VolumeId]
  1045  		bd := make(map[string]interface{})
  1046  
  1047  		if instanceBd.Ebs != nil && instanceBd.Ebs.DeleteOnTermination != nil {
  1048  			bd["delete_on_termination"] = *instanceBd.Ebs.DeleteOnTermination
  1049  		}
  1050  		if vol.Size != nil {
  1051  			bd["volume_size"] = *vol.Size
  1052  		}
  1053  		if vol.VolumeType != nil {
  1054  			bd["volume_type"] = *vol.VolumeType
  1055  		}
  1056  		if vol.Iops != nil {
  1057  			bd["iops"] = *vol.Iops
  1058  		}
  1059  
  1060  		if blockDeviceIsRoot(instanceBd, instance) {
  1061  			blockDevices["root"] = bd
  1062  		} else {
  1063  			if instanceBd.DeviceName != nil {
  1064  				bd["device_name"] = *instanceBd.DeviceName
  1065  			}
  1066  			if vol.Encrypted != nil {
  1067  				bd["encrypted"] = *vol.Encrypted
  1068  			}
  1069  			if vol.SnapshotId != nil {
  1070  				bd["snapshot_id"] = *vol.SnapshotId
  1071  			}
  1072  
  1073  			blockDevices["ebs"] = append(blockDevices["ebs"].([]map[string]interface{}), bd)
  1074  		}
  1075  	}
  1076  
  1077  	return blockDevices, nil
  1078  }
  1079  
  1080  func blockDeviceIsRoot(bd *ec2.InstanceBlockDeviceMapping, instance *ec2.Instance) bool {
  1081  	return bd.DeviceName != nil &&
  1082  		instance.RootDeviceName != nil &&
  1083  		*bd.DeviceName == *instance.RootDeviceName
  1084  }
  1085  
  1086  func fetchRootDeviceName(ami string, conn *ec2.EC2) (*string, error) {
  1087  	if ami == "" {
  1088  		return nil, errors.New("Cannot fetch root device name for blank AMI ID.")
  1089  	}
  1090  
  1091  	log.Printf("[DEBUG] Describing AMI %q to get root block device name", ami)
  1092  	res, err := conn.DescribeImages(&ec2.DescribeImagesInput{
  1093  		ImageIds: []*string{aws.String(ami)},
  1094  	})
  1095  	if err != nil {
  1096  		return nil, err
  1097  	}
  1098  
  1099  	// For a bad image, we just return nil so we don't block a refresh
  1100  	if len(res.Images) == 0 {
  1101  		return nil, nil
  1102  	}
  1103  
  1104  	image := res.Images[0]
  1105  	rootDeviceName := image.RootDeviceName
  1106  
  1107  	// Instance store backed AMIs do not provide a root device name.
  1108  	if *image.RootDeviceType == ec2.DeviceTypeInstanceStore {
  1109  		return nil, nil
  1110  	}
  1111  
  1112  	// Some AMIs have a RootDeviceName like "/dev/sda1" that does not appear as a
  1113  	// DeviceName in the BlockDeviceMapping list (which will instead have
  1114  	// something like "/dev/sda")
  1115  	//
  1116  	// While this seems like it breaks an invariant of AMIs, it ends up working
  1117  	// on the AWS side, and AMIs like this are common enough that we need to
  1118  	// special case it so Terraform does the right thing.
  1119  	//
  1120  	// Our heuristic is: if the RootDeviceName does not appear in the
  1121  	// BlockDeviceMapping, assume that the DeviceName of the first
  1122  	// BlockDeviceMapping entry serves as the root device.
  1123  	rootDeviceNameInMapping := false
  1124  	for _, bdm := range image.BlockDeviceMappings {
  1125  		if bdm.DeviceName == image.RootDeviceName {
  1126  			rootDeviceNameInMapping = true
  1127  		}
  1128  	}
  1129  
  1130  	if !rootDeviceNameInMapping && len(image.BlockDeviceMappings) > 0 {
  1131  		rootDeviceName = image.BlockDeviceMappings[0].DeviceName
  1132  	}
  1133  
  1134  	if rootDeviceName == nil {
  1135  		return nil, fmt.Errorf("[WARN] Error finding Root Device Name for AMI (%s)", ami)
  1136  	}
  1137  
  1138  	return rootDeviceName, nil
  1139  }
  1140  
  1141  func buildNetworkInterfaceOpts(d *schema.ResourceData, groups []*string, nInterfaces interface{}) []*ec2.InstanceNetworkInterfaceSpecification {
  1142  	networkInterfaces := []*ec2.InstanceNetworkInterfaceSpecification{}
  1143  	// Get necessary items
  1144  	associatePublicIPAddress := d.Get("associate_public_ip_address").(bool)
  1145  	subnet, hasSubnet := d.GetOk("subnet_id")
  1146  
  1147  	if hasSubnet && associatePublicIPAddress {
  1148  		// If we have a non-default VPC / Subnet specified, we can flag
  1149  		// AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided.
  1150  		// You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise
  1151  		// you get: Network interfaces and an instance-level subnet ID may not be specified on the same request
  1152  		// You also need to attach Security Groups to the NetworkInterface instead of the instance,
  1153  		// to avoid: Network interfaces and an instance-level security groups may not be specified on
  1154  		// the same request
  1155  		ni := &ec2.InstanceNetworkInterfaceSpecification{
  1156  			AssociatePublicIpAddress: aws.Bool(associatePublicIPAddress),
  1157  			DeviceIndex:              aws.Int64(int64(0)),
  1158  			SubnetId:                 aws.String(subnet.(string)),
  1159  			Groups:                   groups,
  1160  		}
  1161  
  1162  		if v, ok := d.GetOk("private_ip"); ok {
  1163  			ni.PrivateIpAddress = aws.String(v.(string))
  1164  		}
  1165  
  1166  		if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
  1167  			for _, v := range v.List() {
  1168  				ni.Groups = append(ni.Groups, aws.String(v.(string)))
  1169  			}
  1170  		}
  1171  
  1172  		networkInterfaces = append(networkInterfaces, ni)
  1173  	} else {
  1174  		// If we have manually specified network interfaces, build and attach those here.
  1175  		vL := nInterfaces.(*schema.Set).List()
  1176  		for _, v := range vL {
  1177  			ini := v.(map[string]interface{})
  1178  			ni := &ec2.InstanceNetworkInterfaceSpecification{
  1179  				DeviceIndex:         aws.Int64(int64(ini["device_index"].(int))),
  1180  				NetworkInterfaceId:  aws.String(ini["network_interface_id"].(string)),
  1181  				DeleteOnTermination: aws.Bool(ini["delete_on_termination"].(bool)),
  1182  			}
  1183  			networkInterfaces = append(networkInterfaces, ni)
  1184  		}
  1185  	}
  1186  
  1187  	return networkInterfaces
  1188  }
  1189  
  1190  func readBlockDeviceMappingsFromConfig(
  1191  	d *schema.ResourceData, conn *ec2.EC2) ([]*ec2.BlockDeviceMapping, error) {
  1192  	blockDevices := make([]*ec2.BlockDeviceMapping, 0)
  1193  
  1194  	if v, ok := d.GetOk("ebs_block_device"); ok {
  1195  		vL := v.(*schema.Set).List()
  1196  		for _, v := range vL {
  1197  			bd := v.(map[string]interface{})
  1198  			ebs := &ec2.EbsBlockDevice{
  1199  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
  1200  			}
  1201  
  1202  			if v, ok := bd["snapshot_id"].(string); ok && v != "" {
  1203  				ebs.SnapshotId = aws.String(v)
  1204  			}
  1205  
  1206  			if v, ok := bd["encrypted"].(bool); ok && v {
  1207  				ebs.Encrypted = aws.Bool(v)
  1208  			}
  1209  
  1210  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
  1211  				ebs.VolumeSize = aws.Int64(int64(v))
  1212  			}
  1213  
  1214  			if v, ok := bd["volume_type"].(string); ok && v != "" {
  1215  				ebs.VolumeType = aws.String(v)
  1216  				if "io1" == strings.ToLower(v) {
  1217  					// Condition: This parameter is required for requests to create io1
  1218  					// volumes; it is not used in requests to create gp2, st1, sc1, or
  1219  					// standard volumes.
  1220  					// See: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html
  1221  					if v, ok := bd["iops"].(int); ok && v > 0 {
  1222  						ebs.Iops = aws.Int64(int64(v))
  1223  					}
  1224  				}
  1225  			}
  1226  
  1227  			blockDevices = append(blockDevices, &ec2.BlockDeviceMapping{
  1228  				DeviceName: aws.String(bd["device_name"].(string)),
  1229  				Ebs:        ebs,
  1230  			})
  1231  		}
  1232  	}
  1233  
  1234  	if v, ok := d.GetOk("ephemeral_block_device"); ok {
  1235  		vL := v.(*schema.Set).List()
  1236  		for _, v := range vL {
  1237  			bd := v.(map[string]interface{})
  1238  			bdm := &ec2.BlockDeviceMapping{
  1239  				DeviceName:  aws.String(bd["device_name"].(string)),
  1240  				VirtualName: aws.String(bd["virtual_name"].(string)),
  1241  			}
  1242  			if v, ok := bd["no_device"].(bool); ok && v {
  1243  				bdm.NoDevice = aws.String("")
  1244  				// When NoDevice is true, just ignore VirtualName since it's not needed
  1245  				bdm.VirtualName = nil
  1246  			}
  1247  
  1248  			if bdm.NoDevice == nil && aws.StringValue(bdm.VirtualName) == "" {
  1249  				return nil, errors.New("virtual_name cannot be empty when no_device is false or undefined.")
  1250  			}
  1251  
  1252  			blockDevices = append(blockDevices, bdm)
  1253  		}
  1254  	}
  1255  
  1256  	if v, ok := d.GetOk("root_block_device"); ok {
  1257  		vL := v.([]interface{})
  1258  		if len(vL) > 1 {
  1259  			return nil, errors.New("Cannot specify more than one root_block_device.")
  1260  		}
  1261  		for _, v := range vL {
  1262  			bd := v.(map[string]interface{})
  1263  			ebs := &ec2.EbsBlockDevice{
  1264  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
  1265  			}
  1266  
  1267  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
  1268  				ebs.VolumeSize = aws.Int64(int64(v))
  1269  			}
  1270  
  1271  			if v, ok := bd["volume_type"].(string); ok && v != "" {
  1272  				ebs.VolumeType = aws.String(v)
  1273  			}
  1274  
  1275  			if v, ok := bd["iops"].(int); ok && v > 0 && *ebs.VolumeType == "io1" {
  1276  				// Only set the iops attribute if the volume type is io1. Setting otherwise
  1277  				// can trigger a refresh/plan loop based on the computed value that is given
  1278  				// from AWS, and prevent us from specifying 0 as a valid iops.
  1279  				//   See https://github.com/hashicorp/terraform/pull/4146
  1280  				//   See https://github.com/hashicorp/terraform/issues/7765
  1281  				ebs.Iops = aws.Int64(int64(v))
  1282  			} else if v, ok := bd["iops"].(int); ok && v > 0 && *ebs.VolumeType != "io1" {
  1283  				// Message user about incompatibility
  1284  				log.Print("[WARN] IOPs is only valid for storate type io1 for EBS Volumes")
  1285  			}
  1286  
  1287  			if dn, err := fetchRootDeviceName(d.Get("ami").(string), conn); err == nil {
  1288  				if dn == nil {
  1289  					return nil, fmt.Errorf(
  1290  						"Expected 1 AMI for ID: %s, got none",
  1291  						d.Get("ami").(string))
  1292  				}
  1293  
  1294  				blockDevices = append(blockDevices, &ec2.BlockDeviceMapping{
  1295  					DeviceName: dn,
  1296  					Ebs:        ebs,
  1297  				})
  1298  			} else {
  1299  				return nil, err
  1300  			}
  1301  		}
  1302  	}
  1303  
  1304  	return blockDevices, nil
  1305  }
  1306  
  1307  func readVolumeTags(conn *ec2.EC2, d *schema.ResourceData) error {
  1308  	volumeIds, err := getAwsInstanceVolumeIds(conn, d)
  1309  	if err != nil {
  1310  		return err
  1311  	}
  1312  
  1313  	tagsResp, err := conn.DescribeTags(&ec2.DescribeTagsInput{
  1314  		Filters: []*ec2.Filter{
  1315  			{
  1316  				Name:   aws.String("resource-id"),
  1317  				Values: volumeIds,
  1318  			},
  1319  		},
  1320  	})
  1321  	if err != nil {
  1322  		return err
  1323  	}
  1324  
  1325  	var tags []*ec2.Tag
  1326  
  1327  	for _, t := range tagsResp.Tags {
  1328  		tag := &ec2.Tag{
  1329  			Key:   t.Key,
  1330  			Value: t.Value,
  1331  		}
  1332  		tags = append(tags, tag)
  1333  	}
  1334  
  1335  	d.Set("volume_tags", tagsToMap(tags))
  1336  
  1337  	return nil
  1338  }
  1339  
  1340  // Determine whether we're referring to security groups with
  1341  // IDs or names. We use a heuristic to figure this out. By default,
  1342  // we use IDs if we're in a VPC. However, if we previously had an
  1343  // all-name list of security groups, we use names. Or, if we had any
  1344  // IDs, we use IDs.
  1345  func readSecurityGroups(d *schema.ResourceData, instance *ec2.Instance) error {
  1346  	useID := instance.SubnetId != nil && *instance.SubnetId != ""
  1347  	if v := d.Get("security_groups"); v != nil {
  1348  		match := useID
  1349  		sgs := v.(*schema.Set).List()
  1350  		if len(sgs) > 0 {
  1351  			match = false
  1352  			for _, v := range v.(*schema.Set).List() {
  1353  				if strings.HasPrefix(v.(string), "sg-") {
  1354  					match = true
  1355  					break
  1356  				}
  1357  			}
  1358  		}
  1359  
  1360  		useID = match
  1361  	}
  1362  
  1363  	// Build up the security groups
  1364  	sgs := make([]string, 0, len(instance.SecurityGroups))
  1365  	if useID {
  1366  		for _, sg := range instance.SecurityGroups {
  1367  			sgs = append(sgs, *sg.GroupId)
  1368  		}
  1369  		log.Printf("[DEBUG] Setting Security Group IDs: %#v", sgs)
  1370  		if err := d.Set("vpc_security_group_ids", sgs); err != nil {
  1371  			return err
  1372  		}
  1373  		if err := d.Set("security_groups", []string{}); err != nil {
  1374  			return err
  1375  		}
  1376  	} else {
  1377  		for _, sg := range instance.SecurityGroups {
  1378  			sgs = append(sgs, *sg.GroupName)
  1379  		}
  1380  		log.Printf("[DEBUG] Setting Security Group Names: %#v", sgs)
  1381  		if err := d.Set("security_groups", sgs); err != nil {
  1382  			return err
  1383  		}
  1384  		if err := d.Set("vpc_security_group_ids", []string{}); err != nil {
  1385  			return err
  1386  		}
  1387  	}
  1388  	return nil
  1389  }
  1390  
  1391  type awsInstanceOpts struct {
  1392  	BlockDeviceMappings               []*ec2.BlockDeviceMapping
  1393  	DisableAPITermination             *bool
  1394  	EBSOptimized                      *bool
  1395  	Monitoring                        *ec2.RunInstancesMonitoringEnabled
  1396  	IAMInstanceProfile                *ec2.IamInstanceProfileSpecification
  1397  	ImageID                           *string
  1398  	InstanceInitiatedShutdownBehavior *string
  1399  	InstanceType                      *string
  1400  	KeyName                           *string
  1401  	NetworkInterfaces                 []*ec2.InstanceNetworkInterfaceSpecification
  1402  	Placement                         *ec2.Placement
  1403  	PrivateIPAddress                  *string
  1404  	SecurityGroupIDs                  []*string
  1405  	SecurityGroups                    []*string
  1406  	SpotPlacement                     *ec2.SpotPlacement
  1407  	SubnetID                          *string
  1408  	UserData64                        *string
  1409  }
  1410  
  1411  func buildAwsInstanceOpts(
  1412  	d *schema.ResourceData, meta interface{}) (*awsInstanceOpts, error) {
  1413  	conn := meta.(*AWSClient).ec2conn
  1414  
  1415  	opts := &awsInstanceOpts{
  1416  		DisableAPITermination: aws.Bool(d.Get("disable_api_termination").(bool)),
  1417  		EBSOptimized:          aws.Bool(d.Get("ebs_optimized").(bool)),
  1418  		ImageID:               aws.String(d.Get("ami").(string)),
  1419  		InstanceType:          aws.String(d.Get("instance_type").(string)),
  1420  	}
  1421  
  1422  	if v := d.Get("instance_initiated_shutdown_behavior").(string); v != "" {
  1423  		opts.InstanceInitiatedShutdownBehavior = aws.String(v)
  1424  	}
  1425  
  1426  	opts.Monitoring = &ec2.RunInstancesMonitoringEnabled{
  1427  		Enabled: aws.Bool(d.Get("monitoring").(bool)),
  1428  	}
  1429  
  1430  	opts.IAMInstanceProfile = &ec2.IamInstanceProfileSpecification{
  1431  		Name: aws.String(d.Get("iam_instance_profile").(string)),
  1432  	}
  1433  
  1434  	user_data := d.Get("user_data").(string)
  1435  
  1436  	opts.UserData64 = aws.String(base64Encode([]byte(user_data)))
  1437  
  1438  	// check for non-default Subnet, and cast it to a String
  1439  	subnet, hasSubnet := d.GetOk("subnet_id")
  1440  	subnetID := subnet.(string)
  1441  
  1442  	// Placement is used for aws_instance; SpotPlacement is used for
  1443  	// aws_spot_instance_request. They represent the same data. :-|
  1444  	opts.Placement = &ec2.Placement{
  1445  		AvailabilityZone: aws.String(d.Get("availability_zone").(string)),
  1446  		GroupName:        aws.String(d.Get("placement_group").(string)),
  1447  	}
  1448  
  1449  	opts.SpotPlacement = &ec2.SpotPlacement{
  1450  		AvailabilityZone: aws.String(d.Get("availability_zone").(string)),
  1451  		GroupName:        aws.String(d.Get("placement_group").(string)),
  1452  	}
  1453  
  1454  	if v := d.Get("tenancy").(string); v != "" {
  1455  		opts.Placement.Tenancy = aws.String(v)
  1456  	}
  1457  
  1458  	associatePublicIPAddress := d.Get("associate_public_ip_address").(bool)
  1459  
  1460  	var groups []*string
  1461  	if v := d.Get("security_groups"); v != nil {
  1462  		// Security group names.
  1463  		// For a nondefault VPC, you must use security group IDs instead.
  1464  		// See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RunInstances.html
  1465  		sgs := v.(*schema.Set).List()
  1466  		if len(sgs) > 0 && hasSubnet {
  1467  			log.Print("[WARN] Deprecated. Attempting to use 'security_groups' within a VPC instance. Use 'vpc_security_group_ids' instead.")
  1468  		}
  1469  		for _, v := range sgs {
  1470  			str := v.(string)
  1471  			groups = append(groups, aws.String(str))
  1472  		}
  1473  	}
  1474  
  1475  	networkInterfaces, interfacesOk := d.GetOk("network_interface")
  1476  
  1477  	// If setting subnet and public address, OR manual network interfaces, populate those now.
  1478  	if hasSubnet && associatePublicIPAddress || interfacesOk {
  1479  		// Otherwise we're attaching (a) network interface(s)
  1480  		opts.NetworkInterfaces = buildNetworkInterfaceOpts(d, groups, networkInterfaces)
  1481  	} else {
  1482  		// If simply specifying a subnetID, privateIP, Security Groups, or VPC Security Groups, build these now
  1483  		if subnetID != "" {
  1484  			opts.SubnetID = aws.String(subnetID)
  1485  		}
  1486  
  1487  		if v, ok := d.GetOk("private_ip"); ok {
  1488  			opts.PrivateIPAddress = aws.String(v.(string))
  1489  		}
  1490  		if opts.SubnetID != nil &&
  1491  			*opts.SubnetID != "" {
  1492  			opts.SecurityGroupIDs = groups
  1493  		} else {
  1494  			opts.SecurityGroups = groups
  1495  		}
  1496  
  1497  		if v := d.Get("vpc_security_group_ids").(*schema.Set); v.Len() > 0 {
  1498  			for _, v := range v.List() {
  1499  				opts.SecurityGroupIDs = append(opts.SecurityGroupIDs, aws.String(v.(string)))
  1500  			}
  1501  		}
  1502  	}
  1503  
  1504  	if v, ok := d.GetOk("key_name"); ok {
  1505  		opts.KeyName = aws.String(v.(string))
  1506  	}
  1507  
  1508  	blockDevices, err := readBlockDeviceMappingsFromConfig(d, conn)
  1509  	if err != nil {
  1510  		return nil, err
  1511  	}
  1512  	if len(blockDevices) > 0 {
  1513  		opts.BlockDeviceMappings = blockDevices
  1514  	}
  1515  	return opts, nil
  1516  }
  1517  
  1518  func awsTerminateInstance(conn *ec2.EC2, id string) error {
  1519  	log.Printf("[INFO] Terminating instance: %s", id)
  1520  	req := &ec2.TerminateInstancesInput{
  1521  		InstanceIds: []*string{aws.String(id)},
  1522  	}
  1523  	if _, err := conn.TerminateInstances(req); err != nil {
  1524  		return fmt.Errorf("Error terminating instance: %s", err)
  1525  	}
  1526  
  1527  	log.Printf("[DEBUG] Waiting for instance (%s) to become terminated", id)
  1528  
  1529  	stateConf := &resource.StateChangeConf{
  1530  		Pending:    []string{"pending", "running", "shutting-down", "stopped", "stopping"},
  1531  		Target:     []string{"terminated"},
  1532  		Refresh:    InstanceStateRefreshFunc(conn, id),
  1533  		Timeout:    10 * time.Minute,
  1534  		Delay:      10 * time.Second,
  1535  		MinTimeout: 3 * time.Second,
  1536  	}
  1537  
  1538  	_, err := stateConf.WaitForState()
  1539  	if err != nil {
  1540  		return fmt.Errorf(
  1541  			"Error waiting for instance (%s) to terminate: %s", id, err)
  1542  	}
  1543  
  1544  	return nil
  1545  }
  1546  
  1547  func iamInstanceProfileArnToName(ip *ec2.IamInstanceProfile) string {
  1548  	if ip == nil || ip.Arn == nil {
  1549  		return ""
  1550  	}
  1551  	parts := strings.Split(*ip.Arn, "/")
  1552  	return parts[len(parts)-1]
  1553  }
  1554  
  1555  func userDataHashSum(user_data string) string {
  1556  	// Check whether the user_data is not Base64 encoded.
  1557  	// Always calculate hash of base64 decoded value since we
  1558  	// check against double-encoding when setting it
  1559  	v, base64DecodeError := base64.StdEncoding.DecodeString(user_data)
  1560  	if base64DecodeError != nil {
  1561  		v = []byte(user_data)
  1562  	}
  1563  
  1564  	hash := sha1.Sum(v)
  1565  	return hex.EncodeToString(hash[:])
  1566  }
  1567  
  1568  func getAwsInstanceVolumeIds(conn *ec2.EC2, d *schema.ResourceData) ([]*string, error) {
  1569  	volumeIds := make([]*string, 0)
  1570  
  1571  	opts := &ec2.DescribeVolumesInput{
  1572  		Filters: []*ec2.Filter{
  1573  			{
  1574  				Name:   aws.String("attachment.instance-id"),
  1575  				Values: []*string{aws.String(d.Id())},
  1576  			},
  1577  		},
  1578  	}
  1579  
  1580  	resp, err := conn.DescribeVolumes(opts)
  1581  	if err != nil {
  1582  		return nil, err
  1583  	}
  1584  
  1585  	for _, v := range resp.Volumes {
  1586  		volumeIds = append(volumeIds, v.VolumeId)
  1587  	}
  1588  
  1589  	return volumeIds, nil
  1590  }