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