github.com/cbroglie/terraform@v0.7.0-rc3.0.20170410193827-735dfc416d46/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  	layerIds, err = sortListBasedonTFFile(layerIds, d, "layer_ids")
   569  	if err != nil {
   570  		return fmt.Errorf("[DEBUG] Error sorting layer_ids attribute: %#v", err)
   571  	}
   572  	if err := d.Set("layer_ids", layerIds); err != nil {
   573  		return fmt.Errorf("[DEBUG] Error setting layer_ids attribute: %#v, error: %#v", layerIds, err)
   574  	}
   575  	d.Set("os", instance.Os)
   576  	d.Set("platform", instance.Platform)
   577  	d.Set("private_dns", instance.PrivateDns)
   578  	d.Set("private_ip", instance.PrivateIp)
   579  	d.Set("public_dns", instance.PublicDns)
   580  	d.Set("public_ip", instance.PublicIp)
   581  	d.Set("registered_by", instance.RegisteredBy)
   582  	d.Set("reported_agent_version", instance.ReportedAgentVersion)
   583  	d.Set("reported_os_family", instance.ReportedOs.Family)
   584  	d.Set("reported_os_name", instance.ReportedOs.Name)
   585  	d.Set("reported_os_version", instance.ReportedOs.Version)
   586  	d.Set("root_device_type", instance.RootDeviceType)
   587  	d.Set("root_device_volume_id", instance.RootDeviceVolumeId)
   588  	d.Set("ssh_host_dsa_key_fingerprint", instance.SshHostDsaKeyFingerprint)
   589  	d.Set("ssh_host_rsa_key_fingerprint", instance.SshHostRsaKeyFingerprint)
   590  	d.Set("ssh_key_name", instance.SshKeyName)
   591  	d.Set("stack_id", instance.StackId)
   592  	d.Set("status", instance.Status)
   593  	d.Set("subnet_id", instance.SubnetId)
   594  	d.Set("tenancy", instance.Tenancy)
   595  	d.Set("virtualization_type", instance.VirtualizationType)
   596  
   597  	// Read BlockDeviceMapping
   598  	ibds, err := readOpsworksBlockDevices(d, instance, meta)
   599  	if err != nil {
   600  		return err
   601  	}
   602  
   603  	if err := d.Set("ebs_block_device", ibds["ebs"]); err != nil {
   604  		return err
   605  	}
   606  	if err := d.Set("ephemeral_block_device", ibds["ephemeral"]); err != nil {
   607  		return err
   608  	}
   609  	if ibds["root"] != nil {
   610  		if err := d.Set("root_block_device", []interface{}{ibds["root"]}); err != nil {
   611  			return err
   612  		}
   613  	} else {
   614  		d.Set("root_block_device", []interface{}{})
   615  	}
   616  
   617  	// Read Security Groups
   618  	sgs := make([]string, 0, len(instance.SecurityGroupIds))
   619  	for _, sg := range instance.SecurityGroupIds {
   620  		sgs = append(sgs, *sg)
   621  	}
   622  	if err := d.Set("security_group_ids", sgs); err != nil {
   623  		return err
   624  	}
   625  
   626  	return nil
   627  }
   628  
   629  func resourceAwsOpsworksInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   630  	client := meta.(*AWSClient).opsworksconn
   631  
   632  	err := resourceAwsOpsworksInstanceValidate(d)
   633  	if err != nil {
   634  		return err
   635  	}
   636  
   637  	req := &opsworks.CreateInstanceInput{
   638  		AgentVersion:         aws.String(d.Get("agent_version").(string)),
   639  		Architecture:         aws.String(d.Get("architecture").(string)),
   640  		EbsOptimized:         aws.Bool(d.Get("ebs_optimized").(bool)),
   641  		InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
   642  		InstanceType:         aws.String(d.Get("instance_type").(string)),
   643  		LayerIds:             expandStringList(d.Get("layer_ids").([]interface{})),
   644  		StackId:              aws.String(d.Get("stack_id").(string)),
   645  	}
   646  
   647  	if v, ok := d.GetOk("ami_id"); ok {
   648  		req.AmiId = aws.String(v.(string))
   649  		req.Os = aws.String("Custom")
   650  	}
   651  
   652  	if v, ok := d.GetOk("auto_scaling_type"); ok {
   653  		req.AutoScalingType = aws.String(v.(string))
   654  	}
   655  
   656  	if v, ok := d.GetOk("availability_zone"); ok {
   657  		req.AvailabilityZone = aws.String(v.(string))
   658  	}
   659  
   660  	if v, ok := d.GetOk("hostname"); ok {
   661  		req.Hostname = aws.String(v.(string))
   662  	}
   663  
   664  	if v, ok := d.GetOk("os"); ok {
   665  		req.Os = aws.String(v.(string))
   666  	}
   667  
   668  	if v, ok := d.GetOk("root_device_type"); ok {
   669  		req.RootDeviceType = aws.String(v.(string))
   670  	}
   671  
   672  	if v, ok := d.GetOk("ssh_key_name"); ok {
   673  		req.SshKeyName = aws.String(v.(string))
   674  	}
   675  
   676  	if v, ok := d.GetOk("subnet_id"); ok {
   677  		req.SubnetId = aws.String(v.(string))
   678  	}
   679  
   680  	if v, ok := d.GetOk("tenancy"); ok {
   681  		req.Tenancy = aws.String(v.(string))
   682  	}
   683  
   684  	if v, ok := d.GetOk("virtualization_type"); ok {
   685  		req.VirtualizationType = aws.String(v.(string))
   686  	}
   687  
   688  	var blockDevices []*opsworks.BlockDeviceMapping
   689  
   690  	if v, ok := d.GetOk("ebs_block_device"); ok {
   691  		vL := v.(*schema.Set).List()
   692  		for _, v := range vL {
   693  			bd := v.(map[string]interface{})
   694  			ebs := &opsworks.EbsBlockDevice{
   695  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
   696  			}
   697  
   698  			if v, ok := bd["snapshot_id"].(string); ok && v != "" {
   699  				ebs.SnapshotId = aws.String(v)
   700  			}
   701  
   702  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
   703  				ebs.VolumeSize = aws.Int64(int64(v))
   704  			}
   705  
   706  			if v, ok := bd["volume_type"].(string); ok && v != "" {
   707  				ebs.VolumeType = aws.String(v)
   708  			}
   709  
   710  			if v, ok := bd["iops"].(int); ok && v > 0 {
   711  				ebs.Iops = aws.Int64(int64(v))
   712  			}
   713  
   714  			blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{
   715  				DeviceName: aws.String(bd["device_name"].(string)),
   716  				Ebs:        ebs,
   717  			})
   718  		}
   719  	}
   720  
   721  	if v, ok := d.GetOk("ephemeral_block_device"); ok {
   722  		vL := v.(*schema.Set).List()
   723  		for _, v := range vL {
   724  			bd := v.(map[string]interface{})
   725  			blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{
   726  				DeviceName:  aws.String(bd["device_name"].(string)),
   727  				VirtualName: aws.String(bd["virtual_name"].(string)),
   728  			})
   729  		}
   730  	}
   731  
   732  	if v, ok := d.GetOk("root_block_device"); ok {
   733  		vL := v.(*schema.Set).List()
   734  		if len(vL) > 1 {
   735  			return fmt.Errorf("Cannot specify more than one root_block_device.")
   736  		}
   737  		for _, v := range vL {
   738  			bd := v.(map[string]interface{})
   739  			ebs := &opsworks.EbsBlockDevice{
   740  				DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
   741  			}
   742  
   743  			if v, ok := bd["volume_size"].(int); ok && v != 0 {
   744  				ebs.VolumeSize = aws.Int64(int64(v))
   745  			}
   746  
   747  			if v, ok := bd["volume_type"].(string); ok && v != "" {
   748  				ebs.VolumeType = aws.String(v)
   749  			}
   750  
   751  			if v, ok := bd["iops"].(int); ok && v > 0 {
   752  				ebs.Iops = aws.Int64(int64(v))
   753  			}
   754  
   755  			blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{
   756  				DeviceName: aws.String("ROOT_DEVICE"),
   757  				Ebs:        ebs,
   758  			})
   759  		}
   760  	}
   761  
   762  	if len(blockDevices) > 0 {
   763  		req.BlockDeviceMappings = blockDevices
   764  	}
   765  
   766  	log.Printf("[DEBUG] Creating OpsWorks instance")
   767  
   768  	var resp *opsworks.CreateInstanceOutput
   769  
   770  	resp, err = client.CreateInstance(req)
   771  	if err != nil {
   772  		return err
   773  	}
   774  
   775  	if resp.InstanceId == nil {
   776  		return fmt.Errorf("Error launching instance: no instance returned in response")
   777  	}
   778  
   779  	instanceId := *resp.InstanceId
   780  	d.SetId(instanceId)
   781  	d.Set("id", instanceId)
   782  
   783  	if v, ok := d.GetOk("state"); ok && v.(string) == "running" {
   784  		err := startOpsworksInstance(d, meta, true)
   785  		if err != nil {
   786  			return err
   787  		}
   788  	}
   789  
   790  	return resourceAwsOpsworksInstanceRead(d, meta)
   791  }
   792  
   793  func resourceAwsOpsworksInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   794  	client := meta.(*AWSClient).opsworksconn
   795  
   796  	err := resourceAwsOpsworksInstanceValidate(d)
   797  	if err != nil {
   798  		return err
   799  	}
   800  
   801  	req := &opsworks.UpdateInstanceInput{
   802  		AgentVersion:         aws.String(d.Get("agent_version").(string)),
   803  		Architecture:         aws.String(d.Get("architecture").(string)),
   804  		InstanceId:           aws.String(d.Get("id").(string)),
   805  		InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
   806  	}
   807  
   808  	if v, ok := d.GetOk("ami_id"); ok {
   809  		req.AmiId = aws.String(v.(string))
   810  		req.Os = aws.String("Custom")
   811  	}
   812  
   813  	if v, ok := d.GetOk("auto_scaling_type"); ok {
   814  		req.AutoScalingType = aws.String(v.(string))
   815  	}
   816  
   817  	if v, ok := d.GetOk("hostname"); ok {
   818  		req.Hostname = aws.String(v.(string))
   819  	}
   820  
   821  	if v, ok := d.GetOk("instance_type"); ok {
   822  		req.InstanceType = aws.String(v.(string))
   823  	}
   824  
   825  	if v, ok := d.GetOk("layer_ids"); ok {
   826  		req.LayerIds = expandStringList(v.([]interface{}))
   827  	}
   828  
   829  	if v, ok := d.GetOk("os"); ok {
   830  		req.Os = aws.String(v.(string))
   831  	}
   832  
   833  	if v, ok := d.GetOk("ssh_key_name"); ok {
   834  		req.SshKeyName = aws.String(v.(string))
   835  	}
   836  
   837  	log.Printf("[DEBUG] Updating OpsWorks instance: %s", d.Id())
   838  
   839  	_, err = client.UpdateInstance(req)
   840  	if err != nil {
   841  		return err
   842  	}
   843  
   844  	var status string
   845  
   846  	if v, ok := d.GetOk("status"); ok {
   847  		status = v.(string)
   848  	} else {
   849  		status = "stopped"
   850  	}
   851  
   852  	if v, ok := d.GetOk("state"); ok {
   853  		state := v.(string)
   854  		if state == "running" {
   855  			if status == "stopped" || status == "stopping" || status == "shutting_down" {
   856  				err := startOpsworksInstance(d, meta, false)
   857  				if err != nil {
   858  					return err
   859  				}
   860  			}
   861  		} else {
   862  			if status != "stopped" && status != "stopping" && status != "shutting_down" {
   863  				err := stopOpsworksInstance(d, meta, true)
   864  				if err != nil {
   865  					return err
   866  				}
   867  			}
   868  		}
   869  	}
   870  
   871  	return resourceAwsOpsworksInstanceRead(d, meta)
   872  }
   873  
   874  func resourceAwsOpsworksInstanceDelete(d *schema.ResourceData, meta interface{}) error {
   875  	client := meta.(*AWSClient).opsworksconn
   876  
   877  	if v, ok := d.GetOk("status"); ok && v.(string) != "stopped" {
   878  		err := stopOpsworksInstance(d, meta, true)
   879  		if err != nil {
   880  			return err
   881  		}
   882  	}
   883  
   884  	req := &opsworks.DeleteInstanceInput{
   885  		InstanceId:      aws.String(d.Id()),
   886  		DeleteElasticIp: aws.Bool(d.Get("delete_eip").(bool)),
   887  		DeleteVolumes:   aws.Bool(d.Get("delete_ebs").(bool)),
   888  	}
   889  
   890  	log.Printf("[DEBUG] Deleting OpsWorks instance: %s", d.Id())
   891  
   892  	_, err := client.DeleteInstance(req)
   893  	if err != nil {
   894  		return err
   895  	}
   896  
   897  	d.SetId("")
   898  	return nil
   899  }
   900  
   901  func resourceAwsOpsworksInstanceImport(
   902  	d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
   903  	// Neither delete_eip nor delete_ebs can be fetched
   904  	// from any API call, so we need to default to the values
   905  	// we set in the schema by default
   906  	d.Set("delete_ebs", true)
   907  	d.Set("delete_eip", true)
   908  	return []*schema.ResourceData{d}, nil
   909  }
   910  
   911  func startOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error {
   912  	client := meta.(*AWSClient).opsworksconn
   913  
   914  	instanceId := d.Get("id").(string)
   915  
   916  	req := &opsworks.StartInstanceInput{
   917  		InstanceId: aws.String(instanceId),
   918  	}
   919  
   920  	log.Printf("[DEBUG] Starting OpsWorks instance: %s", instanceId)
   921  
   922  	_, err := client.StartInstance(req)
   923  
   924  	if err != nil {
   925  		return err
   926  	}
   927  
   928  	if wait {
   929  		log.Printf("[DEBUG] Waiting for instance (%s) to become running", instanceId)
   930  
   931  		stateConf := &resource.StateChangeConf{
   932  			Pending:    []string{"requested", "pending", "booting", "running_setup"},
   933  			Target:     []string{"online"},
   934  			Refresh:    OpsworksInstanceStateRefreshFunc(client, instanceId),
   935  			Timeout:    10 * time.Minute,
   936  			Delay:      10 * time.Second,
   937  			MinTimeout: 3 * time.Second,
   938  		}
   939  		_, err = stateConf.WaitForState()
   940  		if err != nil {
   941  			return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s",
   942  				instanceId, err)
   943  		}
   944  	}
   945  
   946  	return nil
   947  }
   948  
   949  func stopOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error {
   950  	client := meta.(*AWSClient).opsworksconn
   951  
   952  	instanceId := d.Get("id").(string)
   953  
   954  	req := &opsworks.StopInstanceInput{
   955  		InstanceId: aws.String(instanceId),
   956  	}
   957  
   958  	log.Printf("[DEBUG] Stopping OpsWorks instance: %s", instanceId)
   959  
   960  	_, err := client.StopInstance(req)
   961  
   962  	if err != nil {
   963  		return err
   964  	}
   965  
   966  	if wait {
   967  		log.Printf("[DEBUG] Waiting for instance (%s) to become stopped", instanceId)
   968  
   969  		stateConf := &resource.StateChangeConf{
   970  			Pending:    []string{"stopping", "terminating", "shutting_down", "terminated"},
   971  			Target:     []string{"stopped"},
   972  			Refresh:    OpsworksInstanceStateRefreshFunc(client, instanceId),
   973  			Timeout:    10 * time.Minute,
   974  			Delay:      10 * time.Second,
   975  			MinTimeout: 3 * time.Second,
   976  		}
   977  		_, err = stateConf.WaitForState()
   978  		if err != nil {
   979  			return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s",
   980  				instanceId, err)
   981  		}
   982  	}
   983  
   984  	return nil
   985  }
   986  
   987  func readOpsworksBlockDevices(d *schema.ResourceData, instance *opsworks.Instance, meta interface{}) (
   988  	map[string]interface{}, error) {
   989  
   990  	blockDevices := make(map[string]interface{})
   991  	blockDevices["ebs"] = make([]map[string]interface{}, 0)
   992  	blockDevices["ephemeral"] = make([]map[string]interface{}, 0)
   993  	blockDevices["root"] = nil
   994  
   995  	if len(instance.BlockDeviceMappings) == 0 {
   996  		return nil, nil
   997  	}
   998  
   999  	for _, bdm := range instance.BlockDeviceMappings {
  1000  		bd := make(map[string]interface{})
  1001  		if bdm.Ebs != nil && bdm.Ebs.DeleteOnTermination != nil {
  1002  			bd["delete_on_termination"] = *bdm.Ebs.DeleteOnTermination
  1003  		}
  1004  		if bdm.Ebs != nil && bdm.Ebs.VolumeSize != nil {
  1005  			bd["volume_size"] = *bdm.Ebs.VolumeSize
  1006  		}
  1007  		if bdm.Ebs != nil && bdm.Ebs.VolumeType != nil {
  1008  			bd["volume_type"] = *bdm.Ebs.VolumeType
  1009  		}
  1010  		if bdm.Ebs != nil && bdm.Ebs.Iops != nil {
  1011  			bd["iops"] = *bdm.Ebs.Iops
  1012  		}
  1013  		if bdm.DeviceName != nil && *bdm.DeviceName == "ROOT_DEVICE" {
  1014  			blockDevices["root"] = bd
  1015  		} else {
  1016  			if bdm.DeviceName != nil {
  1017  				bd["device_name"] = *bdm.DeviceName
  1018  			}
  1019  			if bdm.VirtualName != nil {
  1020  				bd["virtual_name"] = *bdm.VirtualName
  1021  				blockDevices["ephemeral"] = append(blockDevices["ephemeral"].([]map[string]interface{}), bd)
  1022  			} else {
  1023  				if bdm.Ebs != nil && bdm.Ebs.SnapshotId != nil {
  1024  					bd["snapshot_id"] = *bdm.Ebs.SnapshotId
  1025  				}
  1026  				blockDevices["ebs"] = append(blockDevices["ebs"].([]map[string]interface{}), bd)
  1027  			}
  1028  		}
  1029  	}
  1030  	return blockDevices, nil
  1031  }
  1032  
  1033  func OpsworksInstanceStateRefreshFunc(conn *opsworks.OpsWorks, instanceID string) resource.StateRefreshFunc {
  1034  	return func() (interface{}, string, error) {
  1035  		resp, err := conn.DescribeInstances(&opsworks.DescribeInstancesInput{
  1036  			InstanceIds: []*string{aws.String(instanceID)},
  1037  		})
  1038  		if err != nil {
  1039  			if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" {
  1040  				// Set this to nil as if we didn't find anything.
  1041  				resp = nil
  1042  			} else {
  1043  				log.Printf("Error on OpsworksInstanceStateRefresh: %s", err)
  1044  				return nil, "", err
  1045  			}
  1046  		}
  1047  
  1048  		if resp == nil || len(resp.Instances) == 0 {
  1049  			// Sometimes AWS just has consistency issues and doesn't see
  1050  			// our instance yet. Return an empty state.
  1051  			return nil, "", nil
  1052  		}
  1053  
  1054  		i := resp.Instances[0]
  1055  		return i, *i.Status, nil
  1056  	}
  1057  }