github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_instance.go (about)

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