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