Add support for Opsworks Instances
New resource checklist - [x] Acceptance testing - [x] Documentation - [x] Well-formed code
This commit is contained in:
parent
a1f7789161
commit
bcd5904eea
|
@ -208,6 +208,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_opsworks_mysql_layer": resourceAwsOpsworksMysqlLayer(),
|
"aws_opsworks_mysql_layer": resourceAwsOpsworksMysqlLayer(),
|
||||||
"aws_opsworks_ganglia_layer": resourceAwsOpsworksGangliaLayer(),
|
"aws_opsworks_ganglia_layer": resourceAwsOpsworksGangliaLayer(),
|
||||||
"aws_opsworks_custom_layer": resourceAwsOpsworksCustomLayer(),
|
"aws_opsworks_custom_layer": resourceAwsOpsworksCustomLayer(),
|
||||||
|
"aws_opsworks_instance": resourceAwsOpsworksInstance(),
|
||||||
"aws_placement_group": resourceAwsPlacementGroup(),
|
"aws_placement_group": resourceAwsPlacementGroup(),
|
||||||
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
|
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
|
||||||
"aws_rds_cluster": resourceAwsRDSCluster(),
|
"aws_rds_cluster": resourceAwsRDSCluster(),
|
||||||
|
|
|
@ -0,0 +1,998 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/opsworks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksInstance() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsOpsworksInstanceCreate,
|
||||||
|
Read: resourceAwsOpsworksInstanceRead,
|
||||||
|
Update: resourceAwsOpsworksInstanceUpdate,
|
||||||
|
Delete: resourceAwsOpsworksInstanceDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"agent_version": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "INHERIT",
|
||||||
|
},
|
||||||
|
|
||||||
|
"ami_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"architecture": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "x86_64",
|
||||||
|
ValidateFunc: validateArchitecture,
|
||||||
|
},
|
||||||
|
|
||||||
|
"auto_scaling_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ValidateFunc: validateAutoScalingType,
|
||||||
|
},
|
||||||
|
|
||||||
|
"availability_zone": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"created_at": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"delete_ebs": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"delete_eip": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ebs_optimized": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ec2_instance_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ecs_cluster_arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"elastic_ip": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"hostname": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"infrastructure_class": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"install_updates_on_boot": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"instance_profile_arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"instance_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"last_service_error_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"layer_ids": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Required: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
|
"os": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"platform": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"private_dns": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"private_ip": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"public_dns": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"public_ip": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"registered_by": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"reported_agent_version": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"reported_os_family": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"reported_os_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"reported_os_version": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"root_device_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
|
ValidateFunc: validateRootDeviceType,
|
||||||
|
},
|
||||||
|
|
||||||
|
"root_device_volume_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"security_group_ids": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
|
"ssh_host_dsa_key_fingerprint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ssh_host_rsa_key_fingerprint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ssh_key_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"stack_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"state": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ValidateFunc: validateState,
|
||||||
|
},
|
||||||
|
|
||||||
|
"status": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"subnet_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"virtualization_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
ValidateFunc: validateVirtualizationType,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ebs_block_device": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"delete_on_termination": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"device_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"iops": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"snapshot_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"volume_size": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"volume_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
|
||||||
|
buf.WriteString(fmt.Sprintf("%s-", m["snapshot_id"].(string)))
|
||||||
|
return hashcode.String(buf.String())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ephemeral_block_device": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"device_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"virtual_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
|
||||||
|
buf.WriteString(fmt.Sprintf("%s-", m["virtual_name"].(string)))
|
||||||
|
return hashcode.String(buf.String())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"root_block_device": &schema.Schema{
|
||||||
|
// TODO: This is a set because we don't support singleton
|
||||||
|
// sub-resources today. We'll enforce that the set only ever has
|
||||||
|
// length zero or one below. When TF gains support for
|
||||||
|
// sub-resources this can be converted.
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
// "You can only modify the volume size, volume type, and Delete on
|
||||||
|
// Termination flag on the block device mapping entry for the root
|
||||||
|
// device volume." - bit.ly/ec2bdmap
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"delete_on_termination": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"iops": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"volume_size": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"volume_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
// there can be only one root device; no need to hash anything
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateArchitecture(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if value != "x86_64" && value != "i386" {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q must be one of \"x86_64\" or \"i386\"", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAutoScalingType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if value != "load" && value != "timer" {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q must be one of \"load\" or \"timer\"", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateRootDeviceType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if value != "ebs" && value != "instance-store" {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q must be one of \"ebs\" or \"instance-store\"", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateState(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if value != "running" && value != "stopped" {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q must be one of \"running\" or \"stopped\"", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateVirtualizationType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if value != "paravirtual" && value != "hvm" {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q must be one of \"paravirtual\" or \"hvm\"", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksInstanceValidate(d *schema.ResourceData) error {
|
||||||
|
if d.HasChange("ami_id") {
|
||||||
|
if v, ok := d.GetOk("os"); ok {
|
||||||
|
if v.(string) != "Custom" {
|
||||||
|
return fmt.Errorf("OS must be \"Custom\" when using using a custom ami_id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := d.GetOk("root_block_device"); ok {
|
||||||
|
return fmt.Errorf("Cannot specify root_block_device when using a custom ami_id.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := d.GetOk("ebs_block_device"); ok {
|
||||||
|
return fmt.Errorf("Cannot specify ebs_block_device when using a custom ami_id.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := d.GetOk("ephemeral_block_device"); ok {
|
||||||
|
return fmt.Errorf("Cannot specify ephemeral_block_device when using a custom ami_id.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
req := &opsworks.DescribeInstancesInput{
|
||||||
|
InstanceIds: []*string{
|
||||||
|
aws.String(d.Id()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Reading OpsWorks instance: %s", d.Id())
|
||||||
|
|
||||||
|
resp, err := client.DescribeInstances(req)
|
||||||
|
if err != nil {
|
||||||
|
if awserr, ok := err.(awserr.Error); ok {
|
||||||
|
if awserr.Code() == "ResourceNotFoundException" {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := resp.Instances[0]
|
||||||
|
instanceId := *instance.InstanceId
|
||||||
|
d.SetId(instanceId)
|
||||||
|
d.Set("agent_version", instance.AgentVersion)
|
||||||
|
d.Set("ami_id", instance.AmiId)
|
||||||
|
d.Set("architecture", instance.Architecture)
|
||||||
|
d.Set("auto_scaling_type", instance.AutoScalingType)
|
||||||
|
d.Set("availability_zone", instance.AvailabilityZone)
|
||||||
|
d.Set("created_at", instance.CreatedAt)
|
||||||
|
d.Set("ebs_optimized", instance.EbsOptimized)
|
||||||
|
d.Set("ec2_instance_id", instance.Ec2InstanceId)
|
||||||
|
d.Set("ecs_cluster_arn", instance.EcsClusterArn)
|
||||||
|
d.Set("elastic_ip", instance.ElasticIp)
|
||||||
|
d.Set("hostname", instance.Hostname)
|
||||||
|
d.Set("infrastructure_class", instance.InfrastructureClass)
|
||||||
|
d.Set("install_updates_on_boot", instance.InstallUpdatesOnBoot)
|
||||||
|
d.Set("id", instanceId)
|
||||||
|
d.Set("instance_profile_arn", instance.InstanceProfileArn)
|
||||||
|
d.Set("instance_type", instance.InstanceType)
|
||||||
|
d.Set("last_service_error_id", instance.LastServiceErrorId)
|
||||||
|
d.Set("layer_ids", instance.LayerIds)
|
||||||
|
d.Set("os", instance.Os)
|
||||||
|
d.Set("platform", instance.Platform)
|
||||||
|
d.Set("private_dns", instance.PrivateDns)
|
||||||
|
d.Set("private_ip", instance.PrivateIp)
|
||||||
|
d.Set("public_dns", instance.PublicDns)
|
||||||
|
d.Set("public_ip", instance.PublicIp)
|
||||||
|
d.Set("registered_by", instance.RegisteredBy)
|
||||||
|
d.Set("reported_agent_version", instance.ReportedAgentVersion)
|
||||||
|
d.Set("reported_os_family", instance.ReportedOs.Family)
|
||||||
|
d.Set("reported_os_name", instance.ReportedOs.Name)
|
||||||
|
d.Set("reported_os_version", instance.ReportedOs.Version)
|
||||||
|
d.Set("root_device_type", instance.RootDeviceType)
|
||||||
|
d.Set("root_device_volume_id", instance.RootDeviceVolumeId)
|
||||||
|
d.Set("ssh_host_dsa_key_fingerprint", instance.SshHostDsaKeyFingerprint)
|
||||||
|
d.Set("ssh_host_rsa_key_fingerprint", instance.SshHostRsaKeyFingerprint)
|
||||||
|
d.Set("ssh_key_name", instance.SshKeyName)
|
||||||
|
d.Set("stack_id", instance.StackId)
|
||||||
|
d.Set("status", instance.Status)
|
||||||
|
d.Set("subnet_id", instance.SubnetId)
|
||||||
|
d.Set("virtualization_type", instance.VirtualizationType)
|
||||||
|
|
||||||
|
// Read BlockDeviceMapping
|
||||||
|
ibds, err := readOpsworksBlockDevices(d, instance, meta)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Set("ebs_block_device", ibds["ebs"]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Set("ephemeral_block_device", ibds["ephemeral"]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ibds["root"] != nil {
|
||||||
|
if err := d.Set("root_block_device", []interface{}{ibds["root"]}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
d.Set("root_block_device", []interface{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read Security Groups
|
||||||
|
sgs := make([]string, 0, len(instance.SecurityGroupIds))
|
||||||
|
for _, sg := range instance.SecurityGroupIds {
|
||||||
|
sgs = append(sgs, *sg)
|
||||||
|
}
|
||||||
|
if err := d.Set("security_group_ids", sgs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
err := resourceAwsOpsworksInstanceValidate(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &opsworks.CreateInstanceInput{
|
||||||
|
AgentVersion: aws.String(d.Get("agent_version").(string)),
|
||||||
|
Architecture: aws.String(d.Get("architecture").(string)),
|
||||||
|
EbsOptimized: aws.Bool(d.Get("ebs_optimized").(bool)),
|
||||||
|
InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
|
||||||
|
InstanceType: aws.String(d.Get("instance_type").(string)),
|
||||||
|
LayerIds: expandStringList(d.Get("layer_ids").([]interface{})),
|
||||||
|
StackId: aws.String(d.Get("stack_id").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ami_id"); ok {
|
||||||
|
req.AmiId = aws.String(v.(string))
|
||||||
|
req.Os = aws.String("Custom")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("auto_scaling_type"); ok {
|
||||||
|
req.AutoScalingType = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("availability_zone"); ok {
|
||||||
|
req.AvailabilityZone = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("hostname"); ok {
|
||||||
|
req.Hostname = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("os"); ok {
|
||||||
|
req.Os = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("root_device_type"); ok {
|
||||||
|
req.RootDeviceType = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ssh_key_name"); ok {
|
||||||
|
req.SshKeyName = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("subnet_id"); ok {
|
||||||
|
req.SubnetId = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("virtualization_type"); ok {
|
||||||
|
req.VirtualizationType = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
var blockDevices []*opsworks.BlockDeviceMapping
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ebs_block_device"); ok {
|
||||||
|
vL := v.(*schema.Set).List()
|
||||||
|
for _, v := range vL {
|
||||||
|
bd := v.(map[string]interface{})
|
||||||
|
ebs := &opsworks.EbsBlockDevice{
|
||||||
|
DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := bd["snapshot_id"].(string); ok && v != "" {
|
||||||
|
ebs.SnapshotId = aws.String(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := bd["volume_size"].(int); ok && v != 0 {
|
||||||
|
ebs.VolumeSize = aws.Int64(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := bd["volume_type"].(string); ok && v != "" {
|
||||||
|
ebs.VolumeType = aws.String(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := bd["iops"].(int); ok && v > 0 {
|
||||||
|
ebs.Iops = aws.Int64(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{
|
||||||
|
DeviceName: aws.String(bd["device_name"].(string)),
|
||||||
|
Ebs: ebs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ephemeral_block_device"); ok {
|
||||||
|
vL := v.(*schema.Set).List()
|
||||||
|
for _, v := range vL {
|
||||||
|
bd := v.(map[string]interface{})
|
||||||
|
blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{
|
||||||
|
DeviceName: aws.String(bd["device_name"].(string)),
|
||||||
|
VirtualName: aws.String(bd["virtual_name"].(string)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("root_block_device"); ok {
|
||||||
|
vL := v.(*schema.Set).List()
|
||||||
|
if len(vL) > 1 {
|
||||||
|
return fmt.Errorf("Cannot specify more than one root_block_device.")
|
||||||
|
}
|
||||||
|
for _, v := range vL {
|
||||||
|
bd := v.(map[string]interface{})
|
||||||
|
ebs := &opsworks.EbsBlockDevice{
|
||||||
|
DeleteOnTermination: aws.Bool(bd["delete_on_termination"].(bool)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := bd["volume_size"].(int); ok && v != 0 {
|
||||||
|
ebs.VolumeSize = aws.Int64(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := bd["volume_type"].(string); ok && v != "" {
|
||||||
|
ebs.VolumeType = aws.String(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := bd["iops"].(int); ok && v > 0 {
|
||||||
|
ebs.Iops = aws.Int64(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
blockDevices = append(blockDevices, &opsworks.BlockDeviceMapping{
|
||||||
|
DeviceName: aws.String("ROOT_DEVICE"),
|
||||||
|
Ebs: ebs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(blockDevices) > 0 {
|
||||||
|
req.BlockDeviceMappings = blockDevices
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating OpsWorks instance")
|
||||||
|
|
||||||
|
var resp *opsworks.CreateInstanceOutput
|
||||||
|
|
||||||
|
resp, err = client.CreateInstance(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceId := *resp.InstanceId
|
||||||
|
d.SetId(instanceId)
|
||||||
|
d.Set("id", instanceId)
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("state"); ok && v.(string) == "running" {
|
||||||
|
err := startOpsworksInstance(d, meta, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsOpsworksInstanceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
err := resourceAwsOpsworksInstanceValidate(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &opsworks.UpdateInstanceInput{
|
||||||
|
AgentVersion: aws.String(d.Get("agent_version").(string)),
|
||||||
|
Architecture: aws.String(d.Get("architecture").(string)),
|
||||||
|
InstanceId: aws.String(d.Get("id").(string)),
|
||||||
|
InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ami_id"); ok {
|
||||||
|
req.AmiId = aws.String(v.(string))
|
||||||
|
req.Os = aws.String("Custom")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("auto_scaling_type"); ok {
|
||||||
|
req.AutoScalingType = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("hostname"); ok {
|
||||||
|
req.Hostname = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("instance_type"); ok {
|
||||||
|
req.InstanceType = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("layer_ids"); ok {
|
||||||
|
req.LayerIds = expandStringList(v.([]interface{}))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("os"); ok {
|
||||||
|
req.Os = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ssh_key_name"); ok {
|
||||||
|
req.SshKeyName = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Updating OpsWorks instance: %s", d.Id())
|
||||||
|
|
||||||
|
_, err = client.UpdateInstance(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var status string
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("status"); ok {
|
||||||
|
status = v.(string)
|
||||||
|
} else {
|
||||||
|
status = "stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("state"); ok {
|
||||||
|
state := v.(string)
|
||||||
|
if state == "running" {
|
||||||
|
if status == "stopped" || status == "stopping" || status == "shutting_down" {
|
||||||
|
err := startOpsworksInstance(d, meta, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if status != "stopped" && status != "stopping" && status != "shutting_down" {
|
||||||
|
err := stopOpsworksInstance(d, meta, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsOpsworksInstanceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksInstanceDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("status"); ok && v.(string) != "stopped" {
|
||||||
|
err := stopOpsworksInstance(d, meta, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &opsworks.DeleteInstanceInput{
|
||||||
|
InstanceId: aws.String(d.Id()),
|
||||||
|
DeleteElasticIp: aws.Bool(d.Get("delete_eip").(bool)),
|
||||||
|
DeleteVolumes: aws.Bool(d.Get("delete_ebs").(bool)),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Deleting OpsWorks instance: %s", d.Id())
|
||||||
|
|
||||||
|
_, err := client.DeleteInstance(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
instanceId := d.Get("id").(string)
|
||||||
|
|
||||||
|
req := &opsworks.StartInstanceInput{
|
||||||
|
InstanceId: aws.String(instanceId),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Starting OpsWorks instance: %s", instanceId)
|
||||||
|
|
||||||
|
_, err := client.StartInstance(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if wait {
|
||||||
|
log.Printf("[DEBUG] Waiting for instance (%s) to become running", instanceId)
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"requested", "pending", "booting", "running_setup"},
|
||||||
|
Target: []string{"online"},
|
||||||
|
Refresh: OpsworksInstanceStateRefreshFunc(client, instanceId),
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 10 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s",
|
||||||
|
instanceId, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopOpsworksInstance(d *schema.ResourceData, meta interface{}, wait bool) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
instanceId := d.Get("id").(string)
|
||||||
|
|
||||||
|
req := &opsworks.StopInstanceInput{
|
||||||
|
InstanceId: aws.String(instanceId),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Stopping OpsWorks instance: %s", instanceId)
|
||||||
|
|
||||||
|
_, err := client.StopInstance(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if wait {
|
||||||
|
log.Printf("[DEBUG] Waiting for instance (%s) to become stopped", instanceId)
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"stopping", "terminating", "shutting_down", "terminated"},
|
||||||
|
Target: []string{"stopped"},
|
||||||
|
Refresh: OpsworksInstanceStateRefreshFunc(client, instanceId),
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 10 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for instance (%s) to become stopped: %s",
|
||||||
|
instanceId, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readOpsworksBlockDevices(d *schema.ResourceData, instance *opsworks.Instance, meta interface{}) (
|
||||||
|
map[string]interface{}, error) {
|
||||||
|
|
||||||
|
blockDevices := make(map[string]interface{})
|
||||||
|
blockDevices["ebs"] = make([]map[string]interface{}, 0)
|
||||||
|
blockDevices["ephemeral"] = make([]map[string]interface{}, 0)
|
||||||
|
blockDevices["root"] = nil
|
||||||
|
|
||||||
|
if len(instance.BlockDeviceMappings) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bdm := range instance.BlockDeviceMappings {
|
||||||
|
bd := make(map[string]interface{})
|
||||||
|
if bdm.Ebs != nil && bdm.Ebs.DeleteOnTermination != nil {
|
||||||
|
bd["delete_on_termination"] = *bdm.Ebs.DeleteOnTermination
|
||||||
|
}
|
||||||
|
if bdm.Ebs != nil && bdm.Ebs.VolumeSize != nil {
|
||||||
|
bd["volume_size"] = *bdm.Ebs.VolumeSize
|
||||||
|
}
|
||||||
|
if bdm.Ebs != nil && bdm.Ebs.VolumeType != nil {
|
||||||
|
bd["volume_type"] = *bdm.Ebs.VolumeType
|
||||||
|
}
|
||||||
|
if bdm.Ebs != nil && bdm.Ebs.Iops != nil {
|
||||||
|
bd["iops"] = *bdm.Ebs.Iops
|
||||||
|
}
|
||||||
|
if bdm.DeviceName != nil && *bdm.DeviceName == "ROOT_DEVICE" {
|
||||||
|
blockDevices["root"] = bd
|
||||||
|
} else {
|
||||||
|
if bdm.DeviceName != nil {
|
||||||
|
bd["device_name"] = *bdm.DeviceName
|
||||||
|
}
|
||||||
|
if bdm.VirtualName != nil {
|
||||||
|
bd["virtual_name"] = *bdm.VirtualName
|
||||||
|
blockDevices["ephemeral"] = append(blockDevices["ephemeral"].([]map[string]interface{}), bd)
|
||||||
|
} else {
|
||||||
|
if bdm.Ebs != nil && bdm.Ebs.SnapshotId != nil {
|
||||||
|
bd["snapshot_id"] = *bdm.Ebs.SnapshotId
|
||||||
|
}
|
||||||
|
blockDevices["ebs"] = append(blockDevices["ebs"].([]map[string]interface{}), bd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blockDevices, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpsworksInstanceStateRefreshFunc(conn *opsworks.OpsWorks, instanceID string) resource.StateRefreshFunc {
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
resp, err := conn.DescribeInstances(&opsworks.DescribeInstancesInput{
|
||||||
|
InstanceIds: []*string{aws.String(instanceID)},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" {
|
||||||
|
// Set this to nil as if we didn't find anything.
|
||||||
|
resp = nil
|
||||||
|
} else {
|
||||||
|
log.Printf("Error on OpsworksInstanceStateRefresh: %s", err)
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp == nil || len(resp.Instances) == 0 {
|
||||||
|
// Sometimes AWS just has consistency issues and doesn't see
|
||||||
|
// our instance yet. Return an empty state.
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
i := resp.Instances[0]
|
||||||
|
return i, *i.Status, nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,211 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/opsworks"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These tests assume the existence of predefined Opsworks IAM roles named `aws-opsworks-ec2-role`
|
||||||
|
// and `aws-opsworks-service-role`.
|
||||||
|
|
||||||
|
func TestAccAWSOpsworksInstance(t *testing.T) {
|
||||||
|
stackName := fmt.Sprintf("tf-%d", acctest.RandInt())
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAwsOpsworksInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAwsOpsworksInstanceConfigCreate(stackName),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "hostname", "tf-acc1",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "instance_type", "t2.micro",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "state", "stopped",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "layer_ids.#", "1",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "install_updates_on_boot", "true",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "architecture", "x86_64",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "os", "Amazon Linux 2014.09", // inherited from opsworks_stack_test
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "root_device_type", "ebs", // inherited from opsworks_stack_test
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "availability_zone", "us-west-2a", // inherited from opsworks_stack_test
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAwsOpsworksInstanceConfigUpdate(stackName),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "hostname", "tf-acc1",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "instance_type", "t2.small",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "layer_ids.#", "2",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_instance.tf-acc", "os", "Amazon Linux 2015.09",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAwsOpsworksInstanceDestroy(s *terraform.State) error {
|
||||||
|
opsworksconn := testAccProvider.Meta().(*AWSClient).opsworksconn
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_opsworks_instance" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
req := &opsworks.DescribeInstancesInput{
|
||||||
|
InstanceIds: []*string{
|
||||||
|
aws.String(rs.Primary.ID),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := opsworksconn.DescribeInstances(req)
|
||||||
|
if err != nil {
|
||||||
|
if awserr, ok := err.(awserr.Error); ok {
|
||||||
|
if awserr.Code() == "ResourceNotFoundException" {
|
||||||
|
// not found, good to go
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Fall through error on OpsWorks instance test")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccAwsOpsworksInstanceConfigCreate(name string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_security_group" "tf-ops-acc-web" {
|
||||||
|
name = "%s-web"
|
||||||
|
ingress {
|
||||||
|
from_port = 80
|
||||||
|
to_port = 80
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_security_group" "tf-ops-acc-php" {
|
||||||
|
name = "%s-php"
|
||||||
|
ingress {
|
||||||
|
from_port = 8080
|
||||||
|
to_port = 8080
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_opsworks_static_web_layer" "tf-acc" {
|
||||||
|
stack_id = "${aws_opsworks_stack.tf-acc.id}"
|
||||||
|
|
||||||
|
custom_security_group_ids = [
|
||||||
|
"${aws_security_group.tf-ops-acc-web.id}",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_opsworks_php_app_layer" "tf-acc" {
|
||||||
|
stack_id = "${aws_opsworks_stack.tf-acc.id}"
|
||||||
|
|
||||||
|
custom_security_group_ids = [
|
||||||
|
"${aws_security_group.tf-ops-acc-php.id}",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_opsworks_instance" "tf-acc" {
|
||||||
|
stack_id = "${aws_opsworks_stack.tf-acc.id}"
|
||||||
|
layer_ids = [
|
||||||
|
"${aws_opsworks_static_web_layer.tf-acc.id}",
|
||||||
|
]
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
state = "stopped"
|
||||||
|
hostname = "tf-acc1"
|
||||||
|
}
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
`, name, name, testAccAwsOpsworksStackConfigVpcCreate(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccAwsOpsworksInstanceConfigUpdate(name string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_security_group" "tf-ops-acc-web" {
|
||||||
|
name = "%s-web"
|
||||||
|
ingress {
|
||||||
|
from_port = 80
|
||||||
|
to_port = 80
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_security_group" "tf-ops-acc-php" {
|
||||||
|
name = "%s-php"
|
||||||
|
ingress {
|
||||||
|
from_port = 8080
|
||||||
|
to_port = 8080
|
||||||
|
protocol = "tcp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_opsworks_static_web_layer" "tf-acc" {
|
||||||
|
stack_id = "${aws_opsworks_stack.tf-acc.id}"
|
||||||
|
|
||||||
|
custom_security_group_ids = [
|
||||||
|
"${aws_security_group.tf-ops-acc-web.id}",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_opsworks_php_app_layer" "tf-acc" {
|
||||||
|
stack_id = "${aws_opsworks_stack.tf-acc.id}"
|
||||||
|
|
||||||
|
custom_security_group_ids = [
|
||||||
|
"${aws_security_group.tf-ops-acc-php.id}",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_opsworks_instance" "tf-acc" {
|
||||||
|
stack_id = "${aws_opsworks_stack.tf-acc.id}"
|
||||||
|
layer_ids = [
|
||||||
|
"${aws_opsworks_static_web_layer.tf-acc.id}",
|
||||||
|
"${aws_opsworks_php_app_layer.tf-acc.id}",
|
||||||
|
]
|
||||||
|
instance_type = "t2.small"
|
||||||
|
state = "stopped"
|
||||||
|
hostname = "tf-acc1"
|
||||||
|
os = "Amazon Linux 2015.09"
|
||||||
|
}
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
`, name, name, testAccAwsOpsworksStackConfigVpcCreate(name))
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
---
|
||||||
|
layout: "aws"
|
||||||
|
page_title: "AWS: aws_opsworks_instance"
|
||||||
|
sidebar_current: "docs-aws-resource-opsworks-instance"
|
||||||
|
description: |-
|
||||||
|
Provides an OpsWorks instance resource.
|
||||||
|
---
|
||||||
|
|
||||||
|
# aws\_opsworks\_instance
|
||||||
|
|
||||||
|
Provides an OpsWorks instance resource.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "aws_opsworks_instance" "my-instance" {
|
||||||
|
stack_id = "${aws_opsworks_stack.my-stack.id}"
|
||||||
|
layer_ids = [
|
||||||
|
"${aws_opsworks_custom_layer.my-layer.id}",
|
||||||
|
]
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
os = "Amazon Linux 2015.09"
|
||||||
|
state = "stopped"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `instance_type` - (Required) The type of instance to start
|
||||||
|
* `stack_id` - (Required) The id of the stack the instance will belong to.
|
||||||
|
* `layer_ids` - (Required) The ids of the layers the instance will belong to.
|
||||||
|
* `state` - (Optional) The desired state of the instance. Can be either `"running"` or `"stopped"`.
|
||||||
|
* `install_updates_on_boot` - (Optional) Controls where to install OS and package updates when the instance boots. Defaults to `true`.
|
||||||
|
* `auto_scaling_type` - (Optional) Creates load-based or time-based instances. If set, can be either: `"load"` or `"timer"`.
|
||||||
|
* `availability_zone` - (Optional) Name of the availability zone where instances will be created
|
||||||
|
by default.
|
||||||
|
* `ebs_optimized` - (Optional) If true, the launched EC2 instance will be EBS-optimized.
|
||||||
|
* `hostname` - (Optional) The instance's host name.
|
||||||
|
* `architecture` - (Optional) Machine architecture for created instances. Can be either `"x86_64"` (the default) or `"i386"`
|
||||||
|
* `ami_id` - (Optional) The AMI to use for the instance. If an AMI is specified, `os` must be `"Custom"`.
|
||||||
|
* `os` - (Optional) Name of operating system that will be installed.
|
||||||
|
* `root_device_type` - (Optional) Name of the type of root device instances will have by default. Can be either `"ebs"` or `"instance-store"`
|
||||||
|
* `ssh_key_name` - (Optional) Name of the SSH keypair that instances will have by default.
|
||||||
|
* `agent_version` - (Optional) The AWS OpsWorks agent to install. Defaults to `"INHERIT"`.
|
||||||
|
* `subnet_id` - (Optional) Subnet ID to attach to
|
||||||
|
* `virtualization_type` - (Optional) Keyword to choose what virtualization mode created instances
|
||||||
|
will use. Can be either `"paravirtual"` or `"hvm"`.
|
||||||
|
* `root_block_device` - (Optional) Customize details about the root block
|
||||||
|
device of the instance. See [Block Devices](#block-devices) below for details.
|
||||||
|
* `ebs_block_device` - (Optional) Additional EBS block devices to attach to the
|
||||||
|
instance. See [Block Devices](#block-devices) below for details.
|
||||||
|
* `ephemeral_block_device` - (Optional) Customize Ephemeral (also known as
|
||||||
|
"Instance Store") volumes on the instance. See [Block Devices](#block-devices) below for details.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="block-devices"></a>
|
||||||
|
## Block devices
|
||||||
|
|
||||||
|
Each of the `*_block_device` attributes controls a portion of the AWS
|
||||||
|
Instance's "Block Device Mapping". It's a good idea to familiarize yourself with [AWS's Block Device
|
||||||
|
Mapping docs](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html)
|
||||||
|
to understand the implications of using these attributes.
|
||||||
|
|
||||||
|
The `root_block_device` mapping supports the following:
|
||||||
|
|
||||||
|
* `volume_type` - (Optional) The type of volume. Can be `"standard"`, `"gp2"`,
|
||||||
|
or `"io1"`. (Default: `"standard"`).
|
||||||
|
* `volume_size` - (Optional) The size of the volume in gigabytes.
|
||||||
|
* `iops` - (Optional) The amount of provisioned
|
||||||
|
[IOPS](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-io-characteristics.html).
|
||||||
|
This must be set with a `volume_type` of `"io1"`.
|
||||||
|
* `delete_on_termination` - (Optional) Whether the volume should be destroyed
|
||||||
|
on instance termination (Default: `true`).
|
||||||
|
|
||||||
|
Modifying any of the `root_block_device` settings requires resource
|
||||||
|
replacement.
|
||||||
|
|
||||||
|
Each `ebs_block_device` supports the following:
|
||||||
|
|
||||||
|
* `device_name` - The name of the device to mount.
|
||||||
|
* `snapshot_id` - (Optional) The Snapshot ID to mount.
|
||||||
|
* `volume_type` - (Optional) The type of volume. Can be `"standard"`, `"gp2"`,
|
||||||
|
or `"io1"`. (Default: `"standard"`).
|
||||||
|
* `volume_size` - (Optional) The size of the volume in gigabytes.
|
||||||
|
* `iops` - (Optional) The amount of provisioned
|
||||||
|
[IOPS](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-io-characteristics.html).
|
||||||
|
This must be set with a `volume_type` of `"io1"`.
|
||||||
|
* `delete_on_termination` - (Optional) Whether the volume should be destroyed
|
||||||
|
on instance termination (Default: `true`).
|
||||||
|
* `encrypted` - (Optional) Enables [EBS
|
||||||
|
encryption](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html)
|
||||||
|
on the volume (Default: `false`). Cannot be used with `snapshot_id`.
|
||||||
|
|
||||||
|
Modifying any `ebs_block_device` currently requires resource replacement.
|
||||||
|
|
||||||
|
Each `ephemeral_block_device` supports the following:
|
||||||
|
|
||||||
|
* `device_name` - The name of the block device to mount on the instance.
|
||||||
|
* `virtual_name` - The [Instance Store Device
|
||||||
|
Name](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html#InstanceStoreDeviceNames)
|
||||||
|
(e.g. `"ephemeral0"`)
|
||||||
|
|
||||||
|
Each AWS Instance type has a different set of Instance Store block devices
|
||||||
|
available for attachment. AWS [publishes a
|
||||||
|
list](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html#StorageOnInstanceTypes)
|
||||||
|
of which ephemeral devices are available on each type. The devices are always
|
||||||
|
identified by the `virtual_name` in the format `"ephemeral{0..N}"`.
|
||||||
|
|
||||||
|
~> **NOTE:** Currently, changes to `*_block_device` configuration of _existing_
|
||||||
|
resources cannot be automatically detected by Terraform. After making updates
|
||||||
|
to block device configuration, resource recreation can be manually triggered by
|
||||||
|
using the [`taint` command](/docs/commands/taint.html).
|
||||||
|
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The id of the OpsWorks instance.
|
||||||
|
* `agent_version` - The AWS OpsWorks agent version.
|
||||||
|
* `availability_zone` - The availability zone of the instance.
|
||||||
|
* `ssh_key_name` - The key name of the instance
|
||||||
|
* `public_dns` - The public DNS name assigned to the instance. For EC2-VPC, this
|
||||||
|
is only available if you've enabled DNS hostnames for your VPC
|
||||||
|
* `public_ip` - The public IP address assigned to the instance, if applicable.
|
||||||
|
* `private_dns` - The private DNS name assigned to the instance. Can only be
|
||||||
|
used inside the Amazon EC2, and only available if you've enabled DNS hostnames
|
||||||
|
for your VPC
|
||||||
|
* `private_ip` - The private IP address assigned to the instance
|
||||||
|
* `subnet_id` - The VPC subnet ID.
|
||||||
|
* `security_group_ids` - The associated security groups.
|
||||||
|
|
|
@ -458,6 +458,10 @@
|
||||||
<a href="/docs/providers/aws/r/opsworks_haproxy_layer.html">aws_opsworks_haproxy_layer</a>
|
<a href="/docs/providers/aws/r/opsworks_haproxy_layer.html">aws_opsworks_haproxy_layer</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-aws-resource-opsworks-instance") %>>
|
||||||
|
<a href="/docs/providers/aws/r/opsworks_instance.html">aws_opsworks_instance</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-aws-resource-opsworks-java-app-layer") %>>
|
<li<%= sidebar_current("docs-aws-resource-opsworks-java-app-layer") %>>
|
||||||
<a href="/docs/providers/aws/r/opsworks_java_app_layer.html">aws_opsworks_java_app_layer</a>
|
<a href="/docs/providers/aws/r/opsworks_java_app_layer.html">aws_opsworks_java_app_layer</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in New Issue