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