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