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