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