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