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