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

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    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/opsworks"
    16  )
    17  
    18  func resourceAwsOpsworksInstance() *schema.Resource {
    19  	return &schema.Resource{
    20  		Create: resourceAwsOpsworksInstanceCreate,
    21  		Read:   resourceAwsOpsworksInstanceRead,
    22  		Update: resourceAwsOpsworksInstanceUpdate,
    23  		Delete: resourceAwsOpsworksInstanceDelete,
    24  		Importer: &schema.ResourceImporter{
    25  			State: resourceAwsOpsworksInstanceImport,
    26  		},
    27  
    28  		Schema: map[string]*schema.Schema{
    29  			"id": {
    30  				Type:     schema.TypeString,
    31  				Computed: true,
    32  			},
    33  
    34  			"agent_version": {
    35  				Type:     schema.TypeString,
    36  				Optional: true,
    37  				Default:  "INHERIT",
    38  			},
    39  
    40  			"ami_id": {
    41  				Type:     schema.TypeString,
    42  				Optional: true,
    43  				Computed: true,
    44  				ForceNew: true,
    45  			},
    46  
    47  			"architecture": {
    48  				Type:         schema.TypeString,
    49  				Optional:     true,
    50  				Default:      "x86_64",
    51  				ValidateFunc: validateArchitecture,
    52  			},
    53  
    54  			"auto_scaling_type": {
    55  				Type:         schema.TypeString,
    56  				Optional:     true,
    57  				ValidateFunc: validateAutoScalingType,
    58  			},
    59  
    60  			"availability_zone": {
    61  				Type:     schema.TypeString,
    62  				Optional: true,
    63  				Computed: true,
    64  				ForceNew: true,
    65  			},
    66  
    67  			"created_at": {
    68  				Type:     schema.TypeString,
    69  				Optional: true,
    70  				Computed: true,
    71  			},
    72  
    73  			"delete_ebs": {
    74  				Type:     schema.TypeBool,
    75  				Optional: true,
    76  				Default:  true,
    77  			},
    78  
    79  			"delete_eip": {
    80  				Type:     schema.TypeBool,
    81  				Optional: true,
    82  				Default:  true,
    83  			},
    84  
    85  			"ebs_optimized": {
    86  				Type:     schema.TypeBool,
    87  				Optional: true,
    88  				Default:  false,
    89  				ForceNew: true,
    90  			},
    91  
    92  			"ec2_instance_id": {
    93  				Type:     schema.TypeString,
    94  				Optional: true,
    95  				Computed: true,
    96  			},
    97  
    98  			"ecs_cluster_arn": {
    99  				Type:     schema.TypeString,
   100  				Optional: true,
   101  				Computed: true,
   102  			},
   103  
   104  			"elastic_ip": {
   105  				Type:     schema.TypeString,
   106  				Optional: true,
   107  				Computed: true,
   108  			},
   109  
   110  			"hostname": {
   111  				Type:     schema.TypeString,
   112  				Optional: true,
   113  				Computed: true,
   114  			},
   115  
   116  			"infrastructure_class": {
   117  				Type:     schema.TypeString,
   118  				Optional: true,
   119  				Computed: true,
   120  			},
   121  
   122  			"install_updates_on_boot": {
   123  				Type:     schema.TypeBool,
   124  				Optional: true,
   125  				Default:  true,
   126  			},
   127  
   128  			"instance_profile_arn": {
   129  				Type:     schema.TypeString,
   130  				Optional: true,
   131  				Computed: true,
   132  			},
   133  
   134  			"instance_type": {
   135  				Type:     schema.TypeString,
   136  				Optional: true,
   137  			},
   138  
   139  			"last_service_error_id": {
   140  				Type:     schema.TypeString,
   141  				Optional: true,
   142  				Computed: true,
   143  			},
   144  
   145  			"layer_ids": {
   146  				Type:     schema.TypeList,
   147  				Required: true,
   148  				Elem:     &schema.Schema{Type: schema.TypeString},
   149  			},
   150  
   151  			"os": {
   152  				Type:     schema.TypeString,
   153  				Optional: true,
   154  				Computed: true,
   155  				ForceNew: true,
   156  			},
   157  
   158  			"platform": {
   159  				Type:     schema.TypeString,
   160  				Optional: true,
   161  				Computed: true,
   162  			},
   163  
   164  			"private_dns": {
   165  				Type:     schema.TypeString,
   166  				Optional: true,
   167  				Computed: true,
   168  			},
   169  
   170  			"private_ip": {
   171  				Type:     schema.TypeString,
   172  				Optional: true,
   173  				Computed: true,
   174  			},
   175  
   176  			"public_dns": {
   177  				Type:     schema.TypeString,
   178  				Optional: true,
   179  				Computed: true,
   180  			},
   181  
   182  			"public_ip": {
   183  				Type:     schema.TypeString,
   184  				Optional: true,
   185  				Computed: true,
   186  			},
   187  
   188  			"registered_by": {
   189  				Type:     schema.TypeString,
   190  				Optional: true,
   191  				Computed: true,
   192  			},
   193  
   194  			"reported_agent_version": {
   195  				Type:     schema.TypeString,
   196  				Optional: true,
   197  				Computed: true,
   198  			},
   199  
   200  			"reported_os_family": {
   201  				Type:     schema.TypeString,
   202  				Optional: true,
   203  				Computed: true,
   204  			},
   205  
   206  			"reported_os_name": {
   207  				Type:     schema.TypeString,
   208  				Optional: true,
   209  				Computed: true,
   210  			},
   211  
   212  			"reported_os_version": {
   213  				Type:     schema.TypeString,
   214  				Optional: true,
   215  				Computed: true,
   216  			},
   217  
   218  			"root_device_type": {
   219  				Type:         schema.TypeString,
   220  				Optional:     true,
   221  				ForceNew:     true,
   222  				Computed:     true,
   223  				ValidateFunc: validateRootDeviceType,
   224  			},
   225  
   226  			"root_device_volume_id": {
   227  				Type:     schema.TypeString,
   228  				Optional: true,
   229  				Computed: true,
   230  			},
   231  
   232  			"security_group_ids": {
   233  				Type:     schema.TypeList,
   234  				Optional: true,
   235  				Computed: true,
   236  				Elem:     &schema.Schema{Type: schema.TypeString},
   237  			},
   238  
   239  			"ssh_host_dsa_key_fingerprint": {
   240  				Type:     schema.TypeString,
   241  				Optional: true,
   242  				Computed: true,
   243  			},
   244  
   245  			"ssh_host_rsa_key_fingerprint": {
   246  				Type:     schema.TypeString,
   247  				Optional: true,
   248  				Computed: true,
   249  			},
   250  
   251  			"ssh_key_name": {
   252  				Type:     schema.TypeString,
   253  				Optional: true,
   254  				Computed: true,
   255  			},
   256  
   257  			"stack_id": {
   258  				Type:     schema.TypeString,
   259  				Required: true,
   260  				ForceNew: true,
   261  			},
   262  
   263  			"state": {
   264  				Type:         schema.TypeString,
   265  				Optional:     true,
   266  				ValidateFunc: validateState,
   267  			},
   268  
   269  			"status": {
   270  				Type:     schema.TypeString,
   271  				Optional: true,
   272  				Computed: true,
   273  			},
   274  
   275  			"subnet_id": {
   276  				Type:     schema.TypeString,
   277  				Optional: true,
   278  				Computed: true,
   279  				ForceNew: true,
   280  			},
   281  
   282  			"tenancy": {
   283  				Type:         schema.TypeString,
   284  				Optional:     true,
   285  				Computed:     true,
   286  				ForceNew:     true,
   287  				ValidateFunc: validateTenancy,
   288  			},
   289  
   290  			"virtualization_type": {
   291  				Type:         schema.TypeString,
   292  				Optional:     true,
   293  				Computed:     true,
   294  				ForceNew:     true,
   295  				ValidateFunc: validateVirtualizationType,
   296  			},
   297  
   298  			"ebs_block_device": {
   299  				Type:     schema.TypeSet,
   300  				Optional: true,
   301  				Computed: true,
   302  				ForceNew: true,
   303  				Elem: &schema.Resource{
   304  					Schema: map[string]*schema.Schema{
   305  						"delete_on_termination": {
   306  							Type:     schema.TypeBool,
   307  							Optional: true,
   308  							Default:  true,
   309  							ForceNew: true,
   310  						},
   311  
   312  						"device_name": {
   313  							Type:     schema.TypeString,
   314  							Required: true,
   315  							ForceNew: true,
   316  						},
   317  
   318  						"iops": {
   319  							Type:     schema.TypeInt,
   320  							Optional: true,
   321  							Computed: true,
   322  							ForceNew: true,
   323  						},
   324  
   325  						"snapshot_id": {
   326  							Type:     schema.TypeString,
   327  							Optional: true,
   328  							Computed: true,
   329  							ForceNew: true,
   330  						},
   331  
   332  						"volume_size": {
   333  							Type:     schema.TypeInt,
   334  							Optional: true,
   335  							Computed: true,
   336  							ForceNew: true,
   337  						},
   338  
   339  						"volume_type": {
   340  							Type:     schema.TypeString,
   341  							Optional: true,
   342  							Computed: true,
   343  							ForceNew: true,
   344  						},
   345  					},
   346  				},
   347  				Set: func(v interface{}) int {
   348  					var buf bytes.Buffer
   349  					m := v.(map[string]interface{})
   350  					buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
   351  					buf.WriteString(fmt.Sprintf("%s-", m["snapshot_id"].(string)))
   352  					return hashcode.String(buf.String())
   353  				},
   354  			},
   355  			"ephemeral_block_device": {
   356  				Type:     schema.TypeSet,
   357  				Optional: true,
   358  				Computed: true,
   359  				ForceNew: true,
   360  				Elem: &schema.Resource{
   361  					Schema: map[string]*schema.Schema{
   362  						"device_name": {
   363  							Type:     schema.TypeString,
   364  							Required: true,
   365  						},
   366  
   367  						"virtual_name": {
   368  							Type:     schema.TypeString,
   369  							Required: true,
   370  						},
   371  					},
   372  				},
   373  				Set: func(v interface{}) int {
   374  					var buf bytes.Buffer
   375  					m := v.(map[string]interface{})
   376  					buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
   377  					buf.WriteString(fmt.Sprintf("%s-", m["virtual_name"].(string)))
   378  					return hashcode.String(buf.String())
   379  				},
   380  			},
   381  
   382  			"root_block_device": {
   383  				// TODO: This is a set because we don't support singleton
   384  				//       sub-resources today. We'll enforce that the set only ever has
   385  				//       length zero or one below. When TF gains support for
   386  				//       sub-resources this can be converted.
   387  				Type:     schema.TypeSet,
   388  				Optional: true,
   389  				Computed: true,
   390  				ForceNew: true,
   391  				Elem: &schema.Resource{
   392  					// "You can only modify the volume size, volume type, and Delete on
   393  					// Termination flag on the block device mapping entry for the root
   394  					// device volume." - bit.ly/ec2bdmap
   395  					Schema: map[string]*schema.Schema{
   396  						"delete_on_termination": {
   397  							Type:     schema.TypeBool,
   398  							Optional: true,
   399  							Default:  true,
   400  							ForceNew: true,
   401  						},
   402  
   403  						"iops": {
   404  							Type:     schema.TypeInt,
   405  							Optional: true,
   406  							Computed: true,
   407  							ForceNew: true,
   408  						},
   409  
   410  						"volume_size": {
   411  							Type:     schema.TypeInt,
   412  							Optional: true,
   413  							Computed: true,
   414  							ForceNew: true,
   415  						},
   416  
   417  						"volume_type": {
   418  							Type:     schema.TypeString,
   419  							Optional: true,
   420  							Computed: true,
   421  							ForceNew: true,
   422  						},
   423  					},
   424  				},
   425  				Set: func(v interface{}) int {
   426  					// there can be only one root device; no need to hash anything
   427  					return 0
   428  				},
   429  			},
   430  		},
   431  	}
   432  }
   433  
   434  func validateArchitecture(v interface{}, k string) (ws []string, errors []error) {
   435  	value := v.(string)
   436  	if value != "x86_64" && value != "i386" {
   437  		errors = append(errors, fmt.Errorf(
   438  			"%q must be one of \"x86_64\" or \"i386\"", k))
   439  	}
   440  	return
   441  }
   442  
   443  func validateTenancy(v interface{}, k string) (ws []string, errors []error) {
   444  	value := v.(string)
   445  	if value != "dedicated" && value != "default" && value != "host" {
   446  		errors = append(errors, fmt.Errorf(
   447  			"%q must be one of \"dedicated\", \"default\" or \"host\"", k))
   448  	}
   449  	return
   450  }
   451  
   452  func validateAutoScalingType(v interface{}, k string) (ws []string, errors []error) {
   453  	value := v.(string)
   454  	if value != "load" && value != "timer" {
   455  		errors = append(errors, fmt.Errorf(
   456  			"%q must be one of \"load\" or \"timer\"", k))
   457  	}
   458  	return
   459  }
   460  
   461  func validateRootDeviceType(v interface{}, k string) (ws []string, errors []error) {
   462  	value := v.(string)
   463  	if value != "ebs" && value != "instance-store" {
   464  		errors = append(errors, fmt.Errorf(
   465  			"%q must be one of \"ebs\" or \"instance-store\"", k))
   466  	}
   467  	return
   468  }
   469  
   470  func validateState(v interface{}, k string) (ws []string, errors []error) {
   471  	value := v.(string)
   472  	if value != "running" && value != "stopped" {
   473  		errors = append(errors, fmt.Errorf(
   474  			"%q must be one of \"running\" or \"stopped\"", k))
   475  	}
   476  	return
   477  }
   478  
   479  func validateVirtualizationType(v interface{}, k string) (ws []string, errors []error) {
   480  	value := v.(string)
   481  	if value != "paravirtual" && value != "hvm" {
   482  		errors = append(errors, fmt.Errorf(
   483  			"%q must be one of \"paravirtual\" or \"hvm\"", k))
   484  	}
   485  	return
   486  }
   487  
   488  func resourceAwsOpsworksInstanceValidate(d *schema.ResourceData) error {
   489  	if d.HasChange("ami_id") {
   490  		if v, ok := d.GetOk("os"); ok {
   491  			if v.(string) != "Custom" {
   492  				return fmt.Errorf("OS must be \"Custom\" when using using a custom ami_id")
   493  			}
   494  		}
   495  
   496  		if _, ok := d.GetOk("root_block_device"); ok {
   497  			return fmt.Errorf("Cannot specify root_block_device when using a custom ami_id.")
   498  		}
   499  
   500  		if _, ok := d.GetOk("ebs_block_device"); ok {
   501  			return fmt.Errorf("Cannot specify ebs_block_device when using a custom ami_id.")
   502  		}
   503  
   504  		if _, ok := d.GetOk("ephemeral_block_device"); ok {
   505  			return fmt.Errorf("Cannot specify ephemeral_block_device when using a custom ami_id.")
   506  		}
   507  	}
   508  	return nil
   509  }
   510  
   511  func resourceAwsOpsworksInstanceRead(d *schema.ResourceData, meta interface{}) error {
   512  	client := meta.(*AWSClient).opsworksconn
   513  
   514  	req := &opsworks.DescribeInstancesInput{
   515  		InstanceIds: []*string{
   516  			aws.String(d.Id()),
   517  		},
   518  	}
   519  
   520  	log.Printf("[DEBUG] Reading OpsWorks instance: %s", d.Id())
   521  
   522  	resp, err := client.DescribeInstances(req)
   523  	if err != nil {
   524  		if awserr, ok := err.(awserr.Error); ok {
   525  			if awserr.Code() == "ResourceNotFoundException" {
   526  				d.SetId("")
   527  				return nil
   528  			}
   529  		}
   530  		return err
   531  	}
   532  
   533  	// If nothing was found, then return no state
   534  	if len(resp.Instances) == 0 {
   535  		d.SetId("")
   536  		return nil
   537  	}
   538  	instance := resp.Instances[0]
   539  
   540  	if instance.InstanceId == nil {
   541  		d.SetId("")
   542  		return nil
   543  	}
   544  	instanceId := *instance.InstanceId
   545  
   546  	d.SetId(instanceId)
   547  	d.Set("agent_version", instance.AgentVersion)
   548  	d.Set("ami_id", instance.AmiId)
   549  	d.Set("architecture", instance.Architecture)
   550  	d.Set("auto_scaling_type", instance.AutoScalingType)
   551  	d.Set("availability_zone", instance.AvailabilityZone)
   552  	d.Set("created_at", instance.CreatedAt)
   553  	d.Set("ebs_optimized", instance.EbsOptimized)
   554  	d.Set("ec2_instance_id", instance.Ec2InstanceId)
   555  	d.Set("ecs_cluster_arn", instance.EcsClusterArn)
   556  	d.Set("elastic_ip", instance.ElasticIp)
   557  	d.Set("hostname", instance.Hostname)
   558  	d.Set("infrastructure_class", instance.InfrastructureClass)
   559  	d.Set("install_updates_on_boot", instance.InstallUpdatesOnBoot)
   560  	d.Set("id", instanceId)
   561  	d.Set("instance_profile_arn", instance.InstanceProfileArn)
   562  	d.Set("instance_type", instance.InstanceType)
   563  	d.Set("last_service_error_id", instance.LastServiceErrorId)
   564  	var layerIds []string
   565  	for _, v := range instance.LayerIds {
   566  		layerIds = append(layerIds, *v)
   567  	}
   568  	if err := d.Set("layer_ids", layerIds); err != nil {
   569  		return fmt.Errorf("[DEBUG] Error setting layer_ids attribute: %#v, error: %#v", layerIds, err)
   570  	}
   571  	d.Set("os", instance.Os)
   572  	d.Set("platform", instance.Platform)
   573  	d.Set("private_dns", instance.PrivateDns)
   574  	d.Set("private_ip", instance.PrivateIp)
   575  	d.Set("public_dns", instance.PublicDns)
   576  	d.Set("public_ip", instance.PublicIp)
   577  	d.Set("registered_by", instance.RegisteredBy)
   578  	d.Set("reported_agent_version", instance.ReportedAgentVersion)
   579  	d.Set("reported_os_family", instance.ReportedOs.Family)
   580  	d.Set("reported_os_name", instance.ReportedOs.Name)
   581  	d.Set("reported_os_version", instance.ReportedOs.Version)
   582  	d.Set("root_device_type", instance.RootDeviceType)
   583  	d.Set("root_device_volume_id", instance.RootDeviceVolumeId)
   584  	d.Set("ssh_host_dsa_key_fingerprint", instance.SshHostDsaKeyFingerprint)
   585  	d.Set("ssh_host_rsa_key_fingerprint", instance.SshHostRsaKeyFingerprint)
   586  	d.Set("ssh_key_name", instance.SshKeyName)
   587  	d.Set("stack_id", instance.StackId)
   588  	d.Set("status", instance.Status)
   589  	d.Set("subnet_id", instance.SubnetId)
   590  	d.Set("tenancy", instance.Tenancy)
   591  	d.Set("virtualization_type", instance.VirtualizationType)
   592  
   593  	// Read BlockDeviceMapping
   594  	ibds, err := readOpsworksBlockDevices(d, instance, meta)
   595  	if err != nil {
   596  		return err
   597  	}
   598  
   599  	if err := d.Set("ebs_block_device", ibds["ebs"]); err != nil {
   600  		return err
   601  	}
   602  	if err := d.Set("ephemeral_block_device", ibds["ephemeral"]); err != nil {
   603  		return err
   604  	}
   605  	if ibds["root"] != nil {
   606  		if err := d.Set("root_block_device", []interface{}{ibds["root"]}); err != nil {
   607  			return err
   608  		}
   609  	} else {
   610  		d.Set("root_block_device", []interface{}{})
   611  	}
   612  
   613  	// Read Security Groups
   614  	sgs := make([]string, 0, len(instance.SecurityGroupIds))
   615  	for _, sg := range instance.SecurityGroupIds {
   616  		sgs = append(sgs, *sg)
   617  	}
   618  	if err := d.Set("security_group_ids", sgs); err != nil {
   619  		return err
   620  	}
   621  
   622  	return nil
   623  }
   624  
   625  func resourceAwsOpsworksInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   626  	client := meta.(*AWSClient).opsworksconn
   627  
   628  	err := resourceAwsOpsworksInstanceValidate(d)
   629  	if err != nil {
   630  		return err
   631  	}
   632  
   633  	req := &opsworks.CreateInstanceInput{
   634  		AgentVersion:         aws.String(d.Get("agent_version").(string)),
   635  		Architecture:         aws.String(d.Get("architecture").(string)),
   636  		EbsOptimized:         aws.Bool(d.Get("ebs_optimized").(bool)),
   637  		InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
   638  		InstanceType:         aws.String(d.Get("instance_type").(string)),
   639  		LayerIds:             expandStringList(d.Get("layer_ids").([]interface{})),
   640  		StackId:              aws.String(d.Get("stack_id").(string)),
   641  	}
   642  
   643  	if v, ok := d.GetOk("ami_id"); ok {
   644  		req.AmiId = aws.String(v.(string))
   645  		req.Os = aws.String("Custom")
   646  	}
   647  
   648  	if v, ok := d.GetOk("auto_scaling_type"); ok {
   649  		req.AutoScalingType = aws.String(v.(string))
   650  	}
   651  
   652  	if v, ok := d.GetOk("availability_zone"); ok {
   653  		req.AvailabilityZone = aws.String(v.(string))
   654  	}
   655  
   656  	if v, ok := d.GetOk("hostname"); ok {
   657  		req.Hostname = aws.String(v.(string))
   658  	}
   659  
   660  	if v, ok := d.GetOk("os"); ok {
   661  		req.Os = aws.String(v.(string))
   662  	}
   663  
   664  	if v, ok := d.GetOk("root_device_type"); ok {
   665  		req.RootDeviceType = aws.String(v.(string))
   666  	}
   667  
   668  	if v, ok := d.GetOk("ssh_key_name"); ok {
   669  		req.SshKeyName = aws.String(v.(string))
   670  	}
   671  
   672  	if v, ok := d.GetOk("subnet_id"); ok {
   673  		req.SubnetId = aws.String(v.(string))
   674  	}
   675  
   676  	if v, ok := d.GetOk("tenancy"); ok {
   677  		req.Tenancy = aws.String(v.(string))
   678  	}
   679  
   680  	if v, ok := d.GetOk("virtualization_type"); ok {
   681  		req.VirtualizationType = aws.String(v.(string))
   682  	}
   683  
   684  	var blockDevices []*opsworks.BlockDeviceMapping
   685  
   686  	if v, ok := d.GetOk("ebs_block_device"); ok {
   687  		vL := v.(*schema.Set).List()
   688  		for _, v := range vL {
   689  			bd := v.(map[string]interface{})
   690  			ebs := &opsworks.EbsBlockDevice{
   691  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
   692  			}
   693  
   694  			if v, ok := bd["snapshot_id"].(string); ok && v != "" {
   695  				ebs.SnapshotId = aws.String(v)
   696  			}
   697  
   698  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
   699  				ebs.VolumeSize = aws.Int64(int64(v))
   700  			}
   701  
   702  			if v, ok := bd["volume_type"].(string); ok && v != "" {
   703  				ebs.VolumeType = aws.String(v)
   704  			}
   705  
   706  			if v, ok := bd["iops"].(int); ok && v > 0 {
   707  				ebs.Iops = aws.Int64(int64(v))
   708  			}
   709  
   710  			blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{
   711  				DeviceName: aws.String(bd["device_name"].(string)),
   712  				Ebs:        ebs,
   713  			})
   714  		}
   715  	}
   716  
   717  	if v, ok := d.GetOk("ephemeral_block_device"); ok {
   718  		vL := v.(*schema.Set).List()
   719  		for _, v := range vL {
   720  			bd := v.(map[string]interface{})
   721  			blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{
   722  				DeviceName:  aws.String(bd["device_name"].(string)),
   723  				VirtualName: aws.String(bd["virtual_name"].(string)),
   724  			})
   725  		}
   726  	}
   727  
   728  	if v, ok := d.GetOk("root_block_device"); ok {
   729  		vL := v.(*schema.Set).List()
   730  		if len(vL) > 1 {
   731  			return fmt.Errorf("Cannot specify more than one root_block_device.")
   732  		}
   733  		for _, v := range vL {
   734  			bd := v.(map[string]interface{})
   735  			ebs := &opsworks.EbsBlockDevice{
   736  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
   737  			}
   738  
   739  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
   740  				ebs.VolumeSize = aws.Int64(int64(v))
   741  			}
   742  
   743  			if v, ok := bd["volume_type"].(string); ok && v != "" {
   744  				ebs.VolumeType = aws.String(v)
   745  			}
   746  
   747  			if v, ok := bd["iops"].(int); ok && v > 0 {
   748  				ebs.Iops = aws.Int64(int64(v))
   749  			}
   750  
   751  			blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{
   752  				DeviceName: aws.String("ROOT_DEVICE"),
   753  				Ebs:        ebs,
   754  			})
   755  		}
   756  	}
   757  
   758  	if len(blockDevices) > 0 {
   759  		req.BlockDeviceMappings = blockDevices
   760  	}
   761  
   762  	log.Printf("[DEBUG] Creating OpsWorks instance")
   763  
   764  	var resp *opsworks.CreateInstanceOutput
   765  
   766  	resp, err = client.CreateInstance(req)
   767  	if err != nil {
   768  		return err
   769  	}
   770  
   771  	if resp.InstanceId == nil {
   772  		return fmt.Errorf("Error launching instance: no instance returned in response")
   773  	}
   774  
   775  	instanceId := *resp.InstanceId
   776  	d.SetId(instanceId)
   777  	d.Set("id", instanceId)
   778  
   779  	if v, ok := d.GetOk("state"); ok && v.(string) == "running" {
   780  		err := startOpsworksInstance(d, meta, false)
   781  		if err != nil {
   782  			return err
   783  		}
   784  	}
   785  
   786  	return resourceAwsOpsworksInstanceRead(d, meta)
   787  }
   788  
   789  func resourceAwsOpsworksInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   790  	client := meta.(*AWSClient).opsworksconn
   791  
   792  	err := resourceAwsOpsworksInstanceValidate(d)
   793  	if err != nil {
   794  		return err
   795  	}
   796  
   797  	req := &opsworks.UpdateInstanceInput{
   798  		AgentVersion:         aws.String(d.Get("agent_version").(string)),
   799  		Architecture:         aws.String(d.Get("architecture").(string)),
   800  		InstanceId:           aws.String(d.Get("id").(string)),
   801  		InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
   802  	}
   803  
   804  	if v, ok := d.GetOk("ami_id"); ok {
   805  		req.AmiId = aws.String(v.(string))
   806  		req.Os = aws.String("Custom")
   807  	}
   808  
   809  	if v, ok := d.GetOk("auto_scaling_type"); ok {
   810  		req.AutoScalingType = aws.String(v.(string))
   811  	}
   812  
   813  	if v, ok := d.GetOk("hostname"); ok {
   814  		req.Hostname = aws.String(v.(string))
   815  	}
   816  
   817  	if v, ok := d.GetOk("instance_type"); ok {
   818  		req.InstanceType = aws.String(v.(string))
   819  	}
   820  
   821  	if v, ok := d.GetOk("layer_ids"); ok {
   822  		req.LayerIds = expandStringList(v.([]interface{}))
   823  
   824  	}
   825  
   826  	if v, ok := d.GetOk("os"); ok {
   827  		req.Os = aws.String(v.(string))
   828  	}
   829  
   830  	if v, ok := d.GetOk("ssh_key_name"); ok {
   831  		req.SshKeyName = aws.String(v.(string))
   832  	}
   833  
   834  	log.Printf("[DEBUG] Updating OpsWorks instance: %s", d.Id())
   835  
   836  	_, err = client.UpdateInstance(req)
   837  	if err != nil {
   838  		return err
   839  	}
   840  
   841  	var status string
   842  
   843  	if v, ok := d.GetOk("status"); ok {
   844  		status = v.(string)
   845  	} else {
   846  		status = "stopped"
   847  	}
   848  
   849  	if v, ok := d.GetOk("state"); ok {
   850  		state := v.(string)
   851  		if state == "running" {
   852  			if status == "stopped" || status == "stopping" || status == "shutting_down" {
   853  				err := startOpsworksInstance(d, meta, false)
   854  				if err != nil {
   855  					return err
   856  				}
   857  			}
   858  		} else {
   859  			if status != "stopped" && status != "stopping" && status != "shutting_down" {
   860  				err := stopOpsworksInstance(d, meta, false)
   861  				if err != nil {
   862  					return err
   863  				}
   864  			}
   865  		}
   866  	}
   867  
   868  	return resourceAwsOpsworksInstanceRead(d, meta)
   869  }
   870  
   871  func resourceAwsOpsworksInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   872  	client := meta.(*AWSClient).opsworksconn
   873  
   874  	if v, ok := d.GetOk("status"); ok && v.(string) != "stopped" {
   875  		err := stopOpsworksInstance(d, meta, true)
   876  		if err != nil {
   877  			return err
   878  		}
   879  	}
   880  
   881  	req := &opsworks.DeleteInstanceInput{
   882  		InstanceId:      aws.String(d.Id()),
   883  		DeleteElasticIp: aws.Bool(d.Get("delete_eip").(bool)),
   884  		DeleteVolumes:   aws.Bool(d.Get("delete_ebs").(bool)),
   885  	}
   886  
   887  	log.Printf("[DEBUG] Deleting OpsWorks instance: %s", d.Id())
   888  
   889  	_, err := client.DeleteInstance(req)
   890  	if err != nil {
   891  		return err
   892  	}
   893  
   894  	d.SetId("")
   895  	return nil
   896  }
   897  
   898  func resourceAwsOpsworksInstanceImport(
   899  	d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
   900  	// Neither delete_eip nor delete_ebs can be fetched
   901  	// from any API call, so we need to default to the values
   902  	// we set in the schema by default
   903  	d.Set("delete_ebs", true)
   904  	d.Set("delete_eip", true)
   905  	return []*schema.ResourceData{d}, nil
   906  }
   907  
   908  func startOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error {
   909  	client := meta.(*AWSClient).opsworksconn
   910  
   911  	instanceId := d.Get("id").(string)
   912  
   913  	req := &opsworks.StartInstanceInput{
   914  		InstanceId: aws.String(instanceId),
   915  	}
   916  
   917  	log.Printf("[DEBUG] Starting OpsWorks instance: %s", instanceId)
   918  
   919  	_, err := client.StartInstance(req)
   920  
   921  	if err != nil {
   922  		return err
   923  	}
   924  
   925  	if wait {
   926  		log.Printf("[DEBUG] Waiting for instance (%s) to become running", instanceId)
   927  
   928  		stateConf := &resource.StateChangeConf{
   929  			Pending:    []string{"requested", "pending", "booting", "running_setup"},
   930  			Target:     []string{"online"},
   931  			Refresh:    OpsworksInstanceStateRefreshFunc(client, instanceId),
   932  			Timeout:    10 * time.Minute,
   933  			Delay:      10 * time.Second,
   934  			MinTimeout: 3 * time.Second,
   935  		}
   936  		_, err = stateConf.WaitForState()
   937  		if err != nil {
   938  			return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s",
   939  				instanceId, err)
   940  		}
   941  	}
   942  
   943  	return nil
   944  }
   945  
   946  func stopOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error {
   947  	client := meta.(*AWSClient).opsworksconn
   948  
   949  	instanceId := d.Get("id").(string)
   950  
   951  	req := &opsworks.StopInstanceInput{
   952  		InstanceId: aws.String(instanceId),
   953  	}
   954  
   955  	log.Printf("[DEBUG] Stopping OpsWorks instance: %s", instanceId)
   956  
   957  	_, err := client.StopInstance(req)
   958  
   959  	if err != nil {
   960  		return err
   961  	}
   962  
   963  	if wait {
   964  		log.Printf("[DEBUG] Waiting for instance (%s) to become stopped", instanceId)
   965  
   966  		stateConf := &resource.StateChangeConf{
   967  			Pending:    []string{"stopping", "terminating", "shutting_down", "terminated"},
   968  			Target:     []string{"stopped"},
   969  			Refresh:    OpsworksInstanceStateRefreshFunc(client, instanceId),
   970  			Timeout:    10 * time.Minute,
   971  			Delay:      10 * time.Second,
   972  			MinTimeout: 3 * time.Second,
   973  		}
   974  		_, err = stateConf.WaitForState()
   975  		if err != nil {
   976  			return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s",
   977  				instanceId, err)
   978  		}
   979  	}
   980  
   981  	return nil
   982  }
   983  
   984  func readOpsworksBlockDevices(d *schema.ResourceData, instance *opsworks.Instance, meta interface{}) (
   985  	map[string]interface{}, error) {
   986  
   987  	blockDevices := make(map[string]interface{})
   988  	blockDevices["ebs"] = make([]map[string]interface{}, 0)
   989  	blockDevices["ephemeral"] = make([]map[string]interface{}, 0)
   990  	blockDevices["root"] = nil
   991  
   992  	if len(instance.BlockDeviceMappings) == 0 {
   993  		return nil, nil
   994  	}
   995  
   996  	for _, bdm := range instance.BlockDeviceMappings {
   997  		bd := make(map[string]interface{})
   998  		if bdm.Ebs != nil && bdm.Ebs.DeleteOnTermination != nil {
   999  			bd["delete_on_termination"] = *bdm.Ebs.DeleteOnTermination
  1000  		}
  1001  		if bdm.Ebs != nil && bdm.Ebs.VolumeSize != nil {
  1002  			bd["volume_size"] = *bdm.Ebs.VolumeSize
  1003  		}
  1004  		if bdm.Ebs != nil && bdm.Ebs.VolumeType != nil {
  1005  			bd["volume_type"] = *bdm.Ebs.VolumeType
  1006  		}
  1007  		if bdm.Ebs != nil && bdm.Ebs.Iops != nil {
  1008  			bd["iops"] = *bdm.Ebs.Iops
  1009  		}
  1010  		if bdm.DeviceName != nil && *bdm.DeviceName == "ROOT_DEVICE" {
  1011  			blockDevices["root"] = bd
  1012  		} else {
  1013  			if bdm.DeviceName != nil {
  1014  				bd["device_name"] = *bdm.DeviceName
  1015  			}
  1016  			if bdm.VirtualName != nil {
  1017  				bd["virtual_name"] = *bdm.VirtualName
  1018  				blockDevices["ephemeral"] = append(blockDevices["ephemeral"].([]map[string]interface{}), bd)
  1019  			} else {
  1020  				if bdm.Ebs != nil && bdm.Ebs.SnapshotId != nil {
  1021  					bd["snapshot_id"] = *bdm.Ebs.SnapshotId
  1022  				}
  1023  				blockDevices["ebs"] = append(blockDevices["ebs"].([]map[string]interface{}), bd)
  1024  			}
  1025  		}
  1026  	}
  1027  	return blockDevices, nil
  1028  }
  1029  
  1030  func OpsworksInstanceStateRefreshFunc(conn *opsworks.OpsWorks, instanceID string) resource.StateRefreshFunc {
  1031  	return func() (interface{}, string, error) {
  1032  		resp, err := conn.DescribeInstances(&opsworks.DescribeInstancesInput{
  1033  			InstanceIds: []*string{aws.String(instanceID)},
  1034  		})
  1035  		if err != nil {
  1036  			if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" {
  1037  				// Set this to nil as if we didn't find anything.
  1038  				resp = nil
  1039  			} else {
  1040  				log.Printf("Error on OpsworksInstanceStateRefresh: %s", err)
  1041  				return nil, "", err
  1042  			}
  1043  		}
  1044  
  1045  		if resp == nil || len(resp.Instances) == 0 {
  1046  			// Sometimes AWS just has consistency issues and doesn't see
  1047  			// our instance yet. Return an empty state.
  1048  			return nil, "", nil
  1049  		}
  1050  
  1051  		i := resp.Instances[0]
  1052  		return i, *i.Status, nil
  1053  	}
  1054  }