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