Merge pull request #2162 from apparentlymart/aws-opsworks-stackslayers

provider/aws: Opsworks Stacks and Layers
This commit is contained in:
Paul Hinze 2015-10-06 15:24:00 -05:00
commit 59c414d257
29 changed files with 2721 additions and 9 deletions

View File

@ -22,6 +22,7 @@ import (
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/kinesis"
"github.com/aws/aws-sdk-go/service/lambda"
"github.com/aws/aws-sdk-go/service/opsworks"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go/service/s3"
@ -61,6 +62,7 @@ type AWSClient struct {
kinesisconn *kinesis.Kinesis
elasticacheconn *elasticache.ElastiCache
lambdaconn *lambda.Lambda
opsworksconn *opsworks.OpsWorks
}
// Client configures and returns a fully initialized AWSClient
@ -106,6 +108,16 @@ func (c *Config) Client() (interface{}, error) {
MaxRetries: aws.Int(c.MaxRetries),
Endpoint: aws.String(c.DynamoDBEndpoint),
}
// Some services exist only in us-east-1, e.g. because they manage
// resources that can span across multiple regions, or because
// signature format v4 requires region to be us-east-1 for global
// endpoints:
// http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
usEast1AwsConfig := &aws.Config{
Credentials: creds,
Region: aws.String("us-east-1"),
MaxRetries: aws.Int(c.MaxRetries),
}
log.Println("[INFO] Initializing DynamoDB connection")
client.dynamodbconn = dynamodb.New(awsDynamoDBConfig)
@ -145,15 +157,8 @@ func (c *Config) Client() (interface{}, error) {
log.Println("[INFO] Initializing EFS Connection")
client.efsconn = efs.New(awsConfig)
// aws-sdk-go uses v4 for signing requests, which requires all global
// endpoints to use 'us-east-1'.
// See http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
log.Println("[INFO] Initializing Route 53 connection")
client.r53conn = route53.New(&aws.Config{
Credentials: creds,
Region: aws.String("us-east-1"),
MaxRetries: aws.Int(c.MaxRetries),
})
client.r53conn = route53.New(usEast1AwsConfig)
log.Println("[INFO] Initializing Elasticache Connection")
client.elasticacheconn = elasticache.New(awsConfig)
@ -166,6 +171,9 @@ func (c *Config) Client() (interface{}, error) {
log.Println("[INFO] Initializing CloudWatch Logs connection")
client.cloudwatchlogsconn = cloudwatchlogs.New(awsConfig)
log.Println("[INFO] Initializing OpsWorks Connection")
client.opsworksconn = opsworks.New(usEast1AwsConfig)
}
if len(errs) > 0 {

View File

@ -0,0 +1,33 @@
package aws
import (
"github.com/awslabs/aws-sdk-go/aws"
"github.com/hashicorp/terraform/helper/schema"
)
func makeAwsStringList(in []interface {}) []*string {
ret := make([]*string, len(in), len(in))
for i := 0; i < len(in); i++ {
ret[i] = aws.String(in[i].(string))
}
return ret
}
func makeAwsStringSet(in *schema.Set) []*string {
inList := in.List()
ret := make([]*string, len(inList), len(inList))
for i := 0; i < len(ret); i++ {
ret[i] = aws.String(inList[i].(string))
}
return ret
}
func unwrapAwsStringList(in []*string) []string {
ret := make([]string, len(in), len(in))
for i := 0; i < len(in); i++ {
if in[i] != nil {
ret[i] = *(in[i])
}
}
return ret
}

View File

@ -0,0 +1,558 @@
package aws
import (
"fmt"
"log"
"strconv"
"github.com/hashicorp/terraform/helper/hashcode"
"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"
)
// OpsWorks has a single concept of "layer" which represents several different
// layer types. The differences between these are in some extra properties that
// get packed into an "Attributes" map, but in the OpsWorks UI these are presented
// as first-class options, and so Terraform prefers to expose them this way and
// hide the implementation detail that they are all packed into a single type
// in the underlying API.
//
// This file contains utilities that are shared between all of the concrete
// layer resource types, which have names matching aws_opsworks_*_layer .
type opsworksLayerTypeAttribute struct {
AttrName string
Type schema.ValueType
Default interface{}
Required bool
WriteOnly bool
}
type opsworksLayerType struct {
TypeName string
DefaultLayerName string
Attributes map[string]*opsworksLayerTypeAttribute
CustomShortName bool
}
var (
opsworksTrueString = "1"
opsworksFalseString = "0"
)
func (lt *opsworksLayerType) SchemaResource() *schema.Resource {
resourceSchema := map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"auto_assign_elastic_ips": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"auto_assign_public_ips": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"custom_instance_profile_arn": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"custom_setup_recipes": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"custom_configure_recipes": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"custom_deploy_recipes": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"custom_undeploy_recipes": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"custom_shutdown_recipes": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"custom_security_group_ids": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"auto_healing": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"install_updates_on_boot": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"instance_shutdown_timeout": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 120,
},
"drain_elb_on_shutdown": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"system_packages": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"stack_id": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
"use_ebs_optimized_instances": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"ebs_volume": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"iops": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 0,
},
"mount_point": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"number_of_disks": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"raid_level": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "",
},
"size": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "standard",
},
},
},
Set: func(v interface{}) int {
m := v.(map[string]interface{})
return hashcode.String(m["mount_point"].(string))
},
},
}
if lt.CustomShortName {
resourceSchema["short_name"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
}
}
if lt.DefaultLayerName != "" {
resourceSchema["name"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: lt.DefaultLayerName,
}
} else {
resourceSchema["name"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
}
}
for key, def := range lt.Attributes {
resourceSchema[key] = &schema.Schema{
Type: def.Type,
Default: def.Default,
Required: def.Required,
Optional: !def.Required,
}
}
return &schema.Resource{
Read: func(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AWSClient).opsworksconn
return lt.Read(d, client)
},
Create: func(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AWSClient).opsworksconn
return lt.Create(d, client)
},
Update: func(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AWSClient).opsworksconn
return lt.Update(d, client)
},
Delete: func(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AWSClient).opsworksconn
return lt.Delete(d, client)
},
Schema: resourceSchema,
}
}
func (lt *opsworksLayerType) Read(d *schema.ResourceData, client *opsworks.OpsWorks) error {
req := &opsworks.DescribeLayersInput{
LayerIds: []*string{
aws.String(d.Id()),
},
}
log.Printf("[DEBUG] Reading OpsWorks layer: %s", d.Id())
resp, err := client.DescribeLayers(req)
if err != nil {
if awserr, ok := err.(awserr.Error); ok {
if awserr.Code() == "ResourceNotFoundException" {
d.SetId("")
return nil
}
}
return err
}
layer := resp.Layers[0]
d.Set("id", layer.LayerId)
d.Set("auto_assign_elastic_ips", layer.AutoAssignElasticIps)
d.Set("auto_assign_public_ips", layer.AutoAssignPublicIps)
d.Set("custom_instance_profile_arn", layer.CustomInstanceProfileArn)
d.Set("custom_security_group_ids", unwrapAwsStringList(layer.CustomSecurityGroupIds))
d.Set("auto_healing", layer.EnableAutoHealing)
d.Set("install_updates_on_boot", layer.InstallUpdatesOnBoot)
d.Set("name", layer.Name)
d.Set("system_packages", unwrapAwsStringList(layer.Packages))
d.Set("stack_id", layer.StackId)
d.Set("use_ebs_optimized_instances", layer.UseEbsOptimizedInstances)
if lt.CustomShortName {
d.Set("short_name", layer.Shortname)
}
lt.SetAttributeMap(d, layer.Attributes)
lt.SetLifecycleEventConfiguration(d, layer.LifecycleEventConfiguration)
lt.SetCustomRecipes(d, layer.CustomRecipes)
lt.SetVolumeConfigurations(d, layer.VolumeConfigurations)
return nil
}
func (lt *opsworksLayerType) Create(d *schema.ResourceData, client *opsworks.OpsWorks) error {
req := &opsworks.CreateLayerInput{
AutoAssignElasticIps: aws.Bool(d.Get("auto_assign_elastic_ips").(bool)),
AutoAssignPublicIps: aws.Bool(d.Get("auto_assign_public_ips").(bool)),
CustomInstanceProfileArn: aws.String(d.Get("custom_instance_profile_arn").(string)),
CustomRecipes: lt.CustomRecipes(d),
CustomSecurityGroupIds: makeAwsStringSet(d.Get("custom_security_group_ids").(*schema.Set)),
EnableAutoHealing: aws.Bool(d.Get("auto_healing").(bool)),
InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
LifecycleEventConfiguration: lt.LifecycleEventConfiguration(d),
Name: aws.String(d.Get("name").(string)),
Packages: makeAwsStringSet(d.Get("system_packages").(*schema.Set)),
Type: aws.String(lt.TypeName),
StackId: aws.String(d.Get("stack_id").(string)),
UseEbsOptimizedInstances: aws.Bool(d.Get("use_ebs_optimized_instances").(bool)),
Attributes: lt.AttributeMap(d),
VolumeConfigurations: lt.VolumeConfigurations(d),
}
if lt.CustomShortName {
req.Shortname = aws.String(d.Get("short_name").(string))
} else {
req.Shortname = aws.String(lt.TypeName)
}
log.Printf("[DEBUG] Creating OpsWorks layer: %s", d.Id())
resp, err := client.CreateLayer(req)
if err != nil {
return err
}
layerId := *resp.LayerId
d.SetId(layerId)
d.Set("id", layerId)
return lt.Read(d, client)
}
func (lt *opsworksLayerType) Update(d *schema.ResourceData, client *opsworks.OpsWorks) error {
req := &opsworks.UpdateLayerInput{
LayerId: aws.String(d.Id()),
AutoAssignElasticIps: aws.Bool(d.Get("auto_assign_elastic_ips").(bool)),
AutoAssignPublicIps: aws.Bool(d.Get("auto_assign_public_ips").(bool)),
CustomInstanceProfileArn: aws.String(d.Get("custom_instance_profile_arn").(string)),
CustomRecipes: lt.CustomRecipes(d),
CustomSecurityGroupIds: makeAwsStringSet(d.Get("custom_security_group_ids").(*schema.Set)),
EnableAutoHealing: aws.Bool(d.Get("auto_healing").(bool)),
InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
LifecycleEventConfiguration: lt.LifecycleEventConfiguration(d),
Name: aws.String(d.Get("name").(string)),
Packages: makeAwsStringSet(d.Get("system_packages").(*schema.Set)),
UseEbsOptimizedInstances: aws.Bool(d.Get("use_ebs_optimized_instances").(bool)),
Attributes: lt.AttributeMap(d),
VolumeConfigurations: lt.VolumeConfigurations(d),
}
if lt.CustomShortName {
req.Shortname = aws.String(d.Get("short_name").(string))
} else {
req.Shortname = aws.String(lt.TypeName)
}
log.Printf("[DEBUG] Updating OpsWorks layer: %s", d.Id())
_, err := client.UpdateLayer(req)
if err != nil {
return err
}
return lt.Read(d, client)
}
func (lt *opsworksLayerType) Delete(d *schema.ResourceData, client *opsworks.OpsWorks) error {
req := &opsworks.DeleteLayerInput{
LayerId: aws.String(d.Id()),
}
log.Printf("[DEBUG] Deleting OpsWorks layer: %s", d.Id())
_, err := client.DeleteLayer(req)
return err
}
func (lt *opsworksLayerType) AttributeMap(d *schema.ResourceData) map[string]*string {
attrs := map[string]*string{}
for key, def := range lt.Attributes {
value := d.Get(key)
switch def.Type {
case schema.TypeString:
strValue := value.(string)
attrs[def.AttrName] = &strValue
case schema.TypeInt:
intValue := value.(int)
strValue := strconv.Itoa(intValue)
attrs[def.AttrName] = &strValue
case schema.TypeBool:
boolValue := value.(bool)
if boolValue {
attrs[def.AttrName] = &opsworksTrueString
} else {
attrs[def.AttrName] = &opsworksFalseString
}
default:
// should never happen
panic(fmt.Errorf("Unsupported OpsWorks layer attribute type"))
}
}
return attrs
}
func (lt *opsworksLayerType) SetAttributeMap(d *schema.ResourceData, attrs map[string]*string) {
for key, def := range lt.Attributes {
// Ignore write-only attributes; we'll just keep what we already have stored.
// (The AWS API returns garbage placeholder values for these.)
if def.WriteOnly {
continue
}
if strPtr, ok := attrs[def.AttrName]; ok && strPtr != nil {
strValue := *strPtr
switch def.Type {
case schema.TypeString:
d.Set(key, strValue)
case schema.TypeInt:
intValue, err := strconv.Atoi(strValue)
if err == nil {
d.Set(key, intValue)
} else {
// Got garbage from the AWS API
d.Set(key, nil)
}
case schema.TypeBool:
boolValue := true
if strValue == opsworksFalseString {
boolValue = false
}
d.Set(key, boolValue)
default:
// should never happen
panic(fmt.Errorf("Unsupported OpsWorks layer attribute type"))
}
return
} else {
d.Set(key, nil)
}
}
}
func (lt *opsworksLayerType) LifecycleEventConfiguration(d *schema.ResourceData) *opsworks.LifecycleEventConfiguration {
return &opsworks.LifecycleEventConfiguration{
Shutdown: &opsworks.ShutdownEventConfiguration{
DelayUntilElbConnectionsDrained: aws.Bool(d.Get("drain_elb_on_shutdown").(bool)),
ExecutionTimeout: aws.Int64(int64(d.Get("instance_shutdown_timeout").(int))),
},
}
}
func (lt *opsworksLayerType) SetLifecycleEventConfiguration(d *schema.ResourceData, v *opsworks.LifecycleEventConfiguration) {
if v == nil || v.Shutdown == nil {
d.Set("drain_elb_on_shutdown", nil)
d.Set("instance_shutdown_timeout", nil)
} else {
d.Set("drain_elb_on_shutdown", v.Shutdown.DelayUntilElbConnectionsDrained)
d.Set("instance_shutdown_timeout", v.Shutdown.ExecutionTimeout)
}
}
func (lt *opsworksLayerType) CustomRecipes(d *schema.ResourceData) *opsworks.Recipes {
return &opsworks.Recipes{
Configure: makeAwsStringList(d.Get("custom_configure_recipes").([]interface{})),
Deploy: makeAwsStringList(d.Get("custom_deploy_recipes").([]interface{})),
Setup: makeAwsStringList(d.Get("custom_setup_recipes").([]interface{})),
Shutdown: makeAwsStringList(d.Get("custom_shutdown_recipes").([]interface{})),
Undeploy: makeAwsStringList(d.Get("custom_undeploy_recipes").([]interface{})),
}
}
func (lt *opsworksLayerType) SetCustomRecipes(d *schema.ResourceData, v *opsworks.Recipes) {
// Null out everything first, and then we'll consider what to put back.
d.Set("custom_configure_recipes", nil)
d.Set("custom_deploy_recipes", nil)
d.Set("custom_setup_recipes", nil)
d.Set("custom_shutdown_recipes", nil)
d.Set("custom_undeploy_recipes", nil)
if v == nil {
return
}
d.Set("custom_configure_recipes", unwrapAwsStringList(v.Configure))
d.Set("custom_deploy_recipes", unwrapAwsStringList(v.Deploy))
d.Set("custom_setup_recipes", unwrapAwsStringList(v.Setup))
d.Set("custom_shutdown_recipes", unwrapAwsStringList(v.Shutdown))
d.Set("custom_undeploy_recipes", unwrapAwsStringList(v.Undeploy))
}
func (lt *opsworksLayerType) VolumeConfigurations(d *schema.ResourceData) []*opsworks.VolumeConfiguration {
configuredVolumes := d.Get("ebs_volume").(*schema.Set).List()
result := make([]*opsworks.VolumeConfiguration, len(configuredVolumes))
for i := 0; i < len(configuredVolumes); i++ {
volumeData := configuredVolumes[i].(map[string]interface{})
result[i] = &opsworks.VolumeConfiguration{
MountPoint: aws.String(volumeData["mount_point"].(string)),
NumberOfDisks: aws.Int64(int64(volumeData["number_of_disks"].(int))),
Size: aws.Int64(int64(volumeData["size"].(int))),
VolumeType: aws.String(volumeData["type"].(string)),
}
iops := int64(volumeData["iops"].(int))
if iops != 0 {
result[i].Iops = aws.Int64(iops)
}
raidLevelStr := volumeData["raid_level"].(string)
if raidLevelStr != "" {
raidLevel, err := strconv.Atoi(raidLevelStr)
if err == nil {
result[i].RaidLevel = aws.Int64(int64(raidLevel))
}
}
}
return result
}
func (lt *opsworksLayerType) SetVolumeConfigurations(d *schema.ResourceData, v []*opsworks.VolumeConfiguration) {
newValue := make([]*map[string]interface{}, len(v))
for i := 0; i < len(v); i++ {
config := v[i]
data := make(map[string]interface{})
newValue[i] = &data
if config.Iops != nil {
data["iops"] = int(*config.Iops)
} else {
data["iops"] = 0
}
if config.MountPoint != nil {
data["mount_point"] = *config.MountPoint
}
if config.NumberOfDisks != nil {
data["number_of_disks"] = int(*config.NumberOfDisks)
}
if config.RaidLevel != nil {
data["raid_level"] = strconv.Itoa(int(*config.RaidLevel))
}
if config.Size != nil {
data["size"] = int(*config.Size)
}
if config.VolumeType != nil {
data["type"] = *config.VolumeType
}
}
d.Set("ebs_volume", newValue)
}

View File

@ -207,6 +207,17 @@ func Provider() terraform.ResourceProvider {
"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),
"aws_network_acl": resourceAwsNetworkAcl(),
"aws_network_interface": resourceAwsNetworkInterface(),
"aws_opsworks_stack": resourceAwsOpsworksStack(),
"aws_opsworks_java_app_layer": resourceAwsOpsworksJavaAppLayer(),
"aws_opsworks_haproxy_layer": resourceAwsOpsworksHaproxyLayer(),
"aws_opsworks_static_web_layer": resourceAwsOpsworksStaticWebLayer(),
"aws_opsworks_php_app_layer": resourceAwsOpsworksPhpAppLayer(),
"aws_opsworks_rails_app_layer": resourceAwsOpsworksRailsAppLayer(),
"aws_opsworks_nodejs_app_layer": resourceAwsOpsworksNodejsAppLayer(),
"aws_opsworks_memcached_layer": resourceAwsOpsworksMemcachedLayer(),
"aws_opsworks_mysql_layer": resourceAwsOpsworksMysqlLayer(),
"aws_opsworks_ganglia_layer": resourceAwsOpsworksGangliaLayer(),
"aws_opsworks_custom_layer": resourceAwsOpsworksCustomLayer(),
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
"aws_route53_delegation_set": resourceAwsRoute53DelegationSet(),
"aws_route53_record": resourceAwsRoute53Record(),

View File

@ -0,0 +1,17 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksCustomLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "custom",
CustomShortName: true,
// The "custom" layer type has no additional attributes
Attributes: map[string]*opsworksLayerTypeAttribute{},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,234 @@
package aws
import (
"fmt"
"testing"
"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 TestAccAwsOpsworksCustomLayer(t *testing.T) {
opsiam := testAccAwsOpsworksStackIam{}
testAccAwsOpsworksStackPopulateIam(t, &opsiam)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsOpsworksCustomLayerDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(testAccAwsOpsworksCustomLayerConfigCreate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "name", "tf-ops-acc-custom-layer",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "auto_assign_elastic_ips", "false",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "auto_healing", "true",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "drain_elb_on_shutdown", "true",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "instance_shutdown_timeout", "300",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "custom_security_group_ids.#", "2",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "system_packages.#", "2",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "system_packages.1368285564", "git",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "system_packages.2937857443", "golang",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.#", "1",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.type", "gp2",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.number_of_disks", "2",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.mount_point", "/home",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.size", "100",
),
),
},
resource.TestStep{
Config: fmt.Sprintf(testAccAwsOpsworksCustomLayerConfigUpdate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "name", "tf-ops-acc-custom-layer",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "drain_elb_on_shutdown", "false",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "instance_shutdown_timeout", "120",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "custom_security_group_ids.#", "3",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "system_packages.#", "3",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "system_packages.1368285564", "git",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "system_packages.2937857443", "golang",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "system_packages.4101929740", "subversion",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.#", "2",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.type", "gp2",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.number_of_disks", "2",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.mount_point", "/home",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.size", "100",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.type", "io1",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.number_of_disks", "4",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.mount_point", "/var",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.size", "100",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.raid_level", "1",
),
resource.TestCheckResourceAttr(
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.iops", "3000",
),
),
},
},
})
}
func testAccCheckAwsOpsworksCustomLayerDestroy(s *terraform.State) error {
if len(s.RootModule().Resources) > 0 {
return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources)
}
return nil
}
var testAccAwsOpsworksCustomLayerSecurityGroups = `
resource "aws_security_group" "tf-ops-acc-layer1" {
name = "tf-ops-acc-layer1"
ingress {
from_port = 8
to_port = -1
protocol = "icmp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "tf-ops-acc-layer2" {
name = "tf-ops-acc-layer2"
ingress {
from_port = 8
to_port = -1
protocol = "icmp"
cidr_blocks = ["0.0.0.0/0"]
}
}
`
var testAccAwsOpsworksCustomLayerConfigCreate = testAccAwsOpsworksStackConfigNoVpcCreate + testAccAwsOpsworksCustomLayerSecurityGroups + `
resource "aws_opsworks_custom_layer" "tf-acc" {
stack_id = "${aws_opsworks_stack.tf-acc.id}"
name = "tf-ops-acc-custom-layer"
short_name = "tf-ops-acc-custom-layer"
auto_assign_public_ips = true
custom_security_group_ids = [
"${aws_security_group.tf-ops-acc-layer1.id}",
"${aws_security_group.tf-ops-acc-layer2.id}",
]
drain_elb_on_shutdown = true
instance_shutdown_timeout = 300
system_packages = [
"git",
"golang",
]
ebs_volume {
type = "gp2"
number_of_disks = 2
mount_point = "/home"
size = 100
raid_level = 0
}
}
`
var testAccAwsOpsworksCustomLayerConfigUpdate = testAccAwsOpsworksStackConfigNoVpcCreate + testAccAwsOpsworksCustomLayerSecurityGroups + `
resource "aws_security_group" "tf-ops-acc-layer3" {
name = "tf-ops-acc-layer3"
ingress {
from_port = 8
to_port = -1
protocol = "icmp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_opsworks_custom_layer" "tf-acc" {
stack_id = "${aws_opsworks_stack.tf-acc.id}"
name = "tf-ops-acc-custom-layer"
short_name = "tf-ops-acc-custom-layer"
auto_assign_public_ips = true
custom_security_group_ids = [
"${aws_security_group.tf-ops-acc-layer1.id}",
"${aws_security_group.tf-ops-acc-layer2.id}",
"${aws_security_group.tf-ops-acc-layer3.id}",
]
drain_elb_on_shutdown = false
instance_shutdown_timeout = 120
system_packages = [
"git",
"golang",
"subversion",
]
ebs_volume {
type = "gp2"
number_of_disks = 2
mount_point = "/home"
size = 100
raid_level = 0
}
ebs_volume {
type = "io1"
number_of_disks = 4
mount_point = "/var"
size = 100
raid_level = 1
iops = 3000
}
}
`

View File

@ -0,0 +1,33 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksGangliaLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "monitoring-master",
DefaultLayerName: "Ganglia",
Attributes: map[string]*opsworksLayerTypeAttribute{
"url": &opsworksLayerTypeAttribute{
AttrName: "GangliaUrl",
Type: schema.TypeString,
Default: "/ganglia",
},
"username": &opsworksLayerTypeAttribute{
AttrName: "GangliaUser",
Type: schema.TypeString,
Default: "opsworks",
},
"password": &opsworksLayerTypeAttribute{
AttrName: "GangliaPassword",
Type: schema.TypeString,
Required: true,
WriteOnly: true,
},
},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,48 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksHaproxyLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "lb",
DefaultLayerName: "HAProxy",
Attributes: map[string]*opsworksLayerTypeAttribute{
"stats_enabled": &opsworksLayerTypeAttribute{
AttrName: "EnableHaproxyStats",
Type: schema.TypeBool,
Default: true,
},
"stats_url": &opsworksLayerTypeAttribute{
AttrName: "HaproxyStatsUrl",
Type: schema.TypeString,
Default: "/haproxy?stats",
},
"stats_user": &opsworksLayerTypeAttribute{
AttrName: "HaproxyStatsUser",
Type: schema.TypeString,
Default: "opsworks",
},
"stats_password": &opsworksLayerTypeAttribute{
AttrName: "HaproxyStatsPassword",
Type: schema.TypeString,
WriteOnly: true,
Required: true,
},
"healthcheck_url": &opsworksLayerTypeAttribute{
AttrName: "HaproxyHealthCheckUrl",
Type: schema.TypeString,
Default: "/",
},
"healthcheck_method": &opsworksLayerTypeAttribute{
AttrName: "HaproxyHealthCheckMethod",
Type: schema.TypeString,
Default: "OPTIONS",
},
},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,42 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksJavaAppLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "java-app",
DefaultLayerName: "Java App Server",
Attributes: map[string]*opsworksLayerTypeAttribute{
"jvm_type": &opsworksLayerTypeAttribute{
AttrName: "Jvm",
Type: schema.TypeString,
Default: "openjdk",
},
"jvm_version": &opsworksLayerTypeAttribute{
AttrName: "JvmVersion",
Type: schema.TypeString,
Default: "7",
},
"jvm_options": &opsworksLayerTypeAttribute{
AttrName: "JvmOptions",
Type: schema.TypeString,
Default: "",
},
"app_server": &opsworksLayerTypeAttribute{
AttrName: "JavaAppServer",
Type: schema.TypeString,
Default: "tomcat",
},
"app_server_version": &opsworksLayerTypeAttribute{
AttrName: "JavaAppServerVersion",
Type: schema.TypeString,
Default: "7",
},
},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,22 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksMemcachedLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "memcached",
DefaultLayerName: "Memcached",
Attributes: map[string]*opsworksLayerTypeAttribute{
"allocated_memory": &opsworksLayerTypeAttribute{
AttrName: "MemcachedMemory",
Type: schema.TypeInt,
Default: 512,
},
},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,27 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksMysqlLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "db-master",
DefaultLayerName: "MySQL",
Attributes: map[string]*opsworksLayerTypeAttribute{
"root_password": &opsworksLayerTypeAttribute{
AttrName: "MysqlRootPassword",
Type: schema.TypeString,
WriteOnly: true,
},
"root_password_on_all_instances": &opsworksLayerTypeAttribute{
AttrName: "MysqlRootPasswordUbiquitous",
Type: schema.TypeBool,
Default: true,
},
},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,22 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksNodejsAppLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "nodejs-app",
DefaultLayerName: "Node.js App Server",
Attributes: map[string]*opsworksLayerTypeAttribute{
"nodejs_version": &opsworksLayerTypeAttribute{
AttrName: "NodejsVersion",
Type: schema.TypeString,
Default: "0.10.38",
},
},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,16 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksPhpAppLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "php-app",
DefaultLayerName: "PHP App Server",
Attributes: map[string]*opsworksLayerTypeAttribute{},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,47 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksRailsAppLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "rails-app",
DefaultLayerName: "Rails App Server",
Attributes: map[string]*opsworksLayerTypeAttribute{
"ruby_version": &opsworksLayerTypeAttribute{
AttrName: "RubyVersion",
Type: schema.TypeString,
Default: "2.0.0",
},
"app_server": &opsworksLayerTypeAttribute{
AttrName: "RailsStack",
Type: schema.TypeString,
Default: "apache_passenger",
},
"passenger_version": &opsworksLayerTypeAttribute{
AttrName: "PassengerVersion",
Type: schema.TypeString,
Default: "4.0.46",
},
"rubygems_version": &opsworksLayerTypeAttribute{
AttrName: "RubygemsVersion",
Type: schema.TypeString,
Default: "2.2.2",
},
"manage_bundler": &opsworksLayerTypeAttribute{
AttrName: "ManageBundler",
Type: schema.TypeBool,
Default: true,
},
"bundler_version": &opsworksLayerTypeAttribute{
AttrName: "BundlerVersion",
Type: schema.TypeString,
Default: "1.5.3",
},
},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,456 @@
package aws
import (
"fmt"
"log"
"strings"
"time"
"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 resourceAwsOpsworksStack() *schema.Resource {
return &schema.Resource{
Create: resourceAwsOpsworksStackCreate,
Read: resourceAwsOpsworksStackRead,
Update: resourceAwsOpsworksStackUpdate,
Delete: resourceAwsOpsworksStackDelete,
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"region": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
"service_role_arn": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"default_instance_profile_arn": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"color": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"configuration_manager_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "Chef",
},
"configuration_manager_version": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "11.4",
},
"manage_berkshelf": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"berkshelf_version": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "3.2.0",
},
"custom_cookbooks_source": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"url": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"username": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"password": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"revision": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"ssh_key": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
},
"custom_json": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"default_availability_zone": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"default_os": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "Ubuntu 12.04 LTS",
},
"default_root_device_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "instance-store",
},
"default_ssh_key_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"default_subnet_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"hostname_theme": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "Layer_Dependent",
},
"use_custom_cookbooks": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"use_opsworks_security_groups": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"vpc_id": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Optional: true,
},
},
}
}
func resourceAwsOpsworksStackValidate(d *schema.ResourceData) error {
cookbooksSourceCount := d.Get("custom_cookbooks_source.#").(int)
if cookbooksSourceCount > 1 {
return fmt.Errorf("Only one custom_cookbooks_source is permitted")
}
vpcId := d.Get("vpc_id").(string)
if vpcId != "" {
if d.Get("default_subnet_id").(string) == "" {
return fmt.Errorf("default_subnet_id must be set if vpc_id is set")
}
} else {
if d.Get("default_availability_zone").(string) == "" {
return fmt.Errorf("either vpc_id or default_availability_zone must be set")
}
}
return nil
}
func resourceAwsOpsworksStackCustomCookbooksSource(d *schema.ResourceData) *opsworks.Source {
count := d.Get("custom_cookbooks_source.#").(int)
if count == 0 {
return nil
}
return &opsworks.Source{
Type: aws.String(d.Get("custom_cookbooks_source.0.type").(string)),
Url: aws.String(d.Get("custom_cookbooks_source.0.url").(string)),
Username: aws.String(d.Get("custom_cookbooks_source.0.username").(string)),
Password: aws.String(d.Get("custom_cookbooks_source.0.password").(string)),
Revision: aws.String(d.Get("custom_cookbooks_source.0.revision").(string)),
SshKey: aws.String(d.Get("custom_cookbooks_source.0.ssh_key").(string)),
}
}
func resourceAwsOpsworksSetStackCustomCookbooksSource(d *schema.ResourceData, v *opsworks.Source) {
nv := make([]interface{}, 0, 1)
if v != nil {
m := make(map[string]interface{})
if v.Type != nil {
m["type"] = *v.Type
}
if v.Url != nil {
m["url"] = *v.Url
}
if v.Username != nil {
m["username"] = *v.Username
}
if v.Password != nil {
m["password"] = *v.Password
}
if v.Revision != nil {
m["revision"] = *v.Revision
}
if v.SshKey != nil {
m["ssh_key"] = *v.SshKey
}
nv = append(nv, m)
}
err := d.Set("custom_cookbooks_source", nv)
if err != nil {
// should never happen
panic(err)
}
}
func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AWSClient).opsworksconn
req := &opsworks.DescribeStacksInput{
StackIds: []*string{
aws.String(d.Id()),
},
}
log.Printf("[DEBUG] Reading OpsWorks stack: %s", d.Id())
resp, err := client.DescribeStacks(req)
if err != nil {
if awserr, ok := err.(awserr.Error); ok {
if awserr.Code() == "ResourceNotFoundException" {
d.SetId("")
return nil
}
}
return err
}
stack := resp.Stacks[0]
d.Set("name", stack.Name)
d.Set("region", stack.Region)
d.Set("default_instance_profile_arn", stack.DefaultInstanceProfileArn)
d.Set("service_role_arn", stack.ServiceRoleArn)
d.Set("default_availability_zone", stack.DefaultAvailabilityZone)
d.Set("default_os", stack.DefaultOs)
d.Set("default_root_device_type", stack.DefaultRootDeviceType)
d.Set("default_ssh_key_name", stack.DefaultSshKeyName)
d.Set("default_subnet_id", stack.DefaultSubnetId)
d.Set("hostname_theme", stack.HostnameTheme)
d.Set("use_custom_cookbooks", stack.UseCustomCookbooks)
d.Set("use_opsworks_security_groups", stack.UseOpsworksSecurityGroups)
d.Set("vpc_id", stack.VpcId)
if color, ok := stack.Attributes["Color"]; ok {
d.Set("color", color)
}
if stack.ConfigurationManager != nil {
d.Set("configuration_manager_name", stack.ConfigurationManager.Name)
d.Set("configuration_manager_version", stack.ConfigurationManager.Version)
}
if stack.ChefConfiguration != nil {
d.Set("berkshelf_version", stack.ChefConfiguration.BerkshelfVersion)
d.Set("manage_berkshelf", stack.ChefConfiguration.ManageBerkshelf)
}
resourceAwsOpsworksSetStackCustomCookbooksSource(d, stack.CustomCookbooksSource)
return nil
}
func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AWSClient).opsworksconn
err := resourceAwsOpsworksStackValidate(d)
if err != nil {
return err
}
req := &opsworks.CreateStackInput{
DefaultInstanceProfileArn: aws.String(d.Get("default_instance_profile_arn").(string)),
Name: aws.String(d.Get("name").(string)),
Region: aws.String(d.Get("region").(string)),
ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)),
}
inVpc := false
if vpcId, ok := d.GetOk("vpc_id"); ok {
req.VpcId = aws.String(vpcId.(string))
inVpc = true
}
if defaultSubnetId, ok := d.GetOk("default_subnet_id"); ok {
req.DefaultSubnetId = aws.String(defaultSubnetId.(string))
}
if defaultAvailabilityZone, ok := d.GetOk("default_availability_zone"); ok {
req.DefaultAvailabilityZone = aws.String(defaultAvailabilityZone.(string))
}
log.Printf("[DEBUG] Creating OpsWorks stack: %s", *req.Name)
var resp *opsworks.CreateStackOutput
err = resource.Retry(20*time.Minute, func() error {
var cerr error
resp, cerr = client.CreateStack(req)
if cerr != nil {
if opserr, ok := cerr.(awserr.Error); ok {
// If Terraform is also managing the service IAM role,
// it may have just been created and not yet be
// propagated.
// AWS doesn't provide a machine-readable code for this
// specific error, so we're forced to do fragile message
// matching.
// The full error we're looking for looks something like
// the following:
// Service Role Arn: [...] is not yet propagated, please try again in a couple of minutes
if opserr.Code() == "ValidationException" && strings.Contains(opserr.Message(), "not yet propagated") {
log.Printf("[INFO] Waiting for service IAM role to propagate")
return cerr
}
}
return resource.RetryError{Err: cerr}
}
return nil
})
if err != nil {
return err
}
stackId := *resp.StackId
d.SetId(stackId)
d.Set("id", stackId)
if inVpc {
// For VPC-based stacks, OpsWorks asynchronously creates some default
// security groups which must exist before layers can be created.
// Unfortunately it doesn't tell us what the ids of these are, so
// we can't actually check for them. Instead, we just wait a nominal
// amount of time for their creation to complete.
log.Print("[INFO] Waiting for OpsWorks built-in security groups to be created")
time.Sleep(30 * time.Second)
}
return resourceAwsOpsworksStackUpdate(d, meta)
}
func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AWSClient).opsworksconn
err := resourceAwsOpsworksStackValidate(d)
if err != nil {
return err
}
req := &opsworks.UpdateStackInput{
CustomJson: aws.String(d.Get("custom_json").(string)),
DefaultInstanceProfileArn: aws.String(d.Get("default_instance_profile_arn").(string)),
DefaultRootDeviceType: aws.String(d.Get("default_root_device_type").(string)),
DefaultSshKeyName: aws.String(d.Get("default_ssh_key_name").(string)),
Name: aws.String(d.Get("name").(string)),
ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)),
StackId: aws.String(d.Id()),
UseCustomCookbooks: aws.Bool(d.Get("use_custom_cookbooks").(bool)),
UseOpsworksSecurityGroups: aws.Bool(d.Get("use_opsworks_security_groups").(bool)),
Attributes: make(map[string]*string),
CustomCookbooksSource: resourceAwsOpsworksStackCustomCookbooksSource(d),
}
if v, ok := d.GetOk("default_os"); ok {
req.DefaultOs = aws.String(v.(string))
}
if v, ok := d.GetOk("default_subnet_id"); ok {
req.DefaultSubnetId = aws.String(v.(string))
}
if v, ok := d.GetOk("default_availability_zone"); ok {
req.DefaultAvailabilityZone = aws.String(v.(string))
}
if v, ok := d.GetOk("hostname_theme"); ok {
req.HostnameTheme = aws.String(v.(string))
}
if v, ok := d.GetOk("color"); ok {
req.Attributes["Color"] = aws.String(v.(string))
}
req.ChefConfiguration = &opsworks.ChefConfiguration{
BerkshelfVersion: aws.String(d.Get("berkshelf_version").(string)),
ManageBerkshelf: aws.Bool(d.Get("manage_berkshelf").(bool)),
}
req.ConfigurationManager = &opsworks.StackConfigurationManager{
Name: aws.String(d.Get("configuration_manager_name").(string)),
Version: aws.String(d.Get("configuration_manager_version").(string)),
}
log.Printf("[DEBUG] Updating OpsWorks stack: %s", d.Id())
_, err = client.UpdateStack(req)
if err != nil {
return err
}
return resourceAwsOpsworksStackRead(d, meta)
}
func resourceAwsOpsworksStackDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AWSClient).opsworksconn
req := &opsworks.DeleteStackInput{
StackId: aws.String(d.Id()),
}
log.Printf("[DEBUG] Deleting OpsWorks stack: %s", d.Id())
_, err := client.DeleteStack(req)
if err != nil {
return err
}
// For a stack in a VPC, OpsWorks has created some default security groups
// in the VPC, which it will now delete.
// Unfortunately, the security groups are deleted asynchronously and there
// is no robust way for us to determine when it is done. The VPC itself
// isn't deletable until the security groups are cleaned up, so this could
// make 'terraform destroy' fail if the VPC is also managed and we don't
// wait for the security groups to be deleted.
// There is no robust way to check for this, so we'll just wait a
// nominal amount of time.
if _, ok := d.GetOk("vpc_id"); ok {
log.Print("[INFO] Waiting for Opsworks built-in security groups to be deleted")
time.Sleep(30 * time.Second)
}
return nil
}

View File

@ -0,0 +1,353 @@
package aws
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/opsworks"
"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`.
///////////////////////////////
//// Tests for the No-VPC case
///////////////////////////////
var testAccAwsOpsworksStackConfigNoVpcCreate = `
resource "aws_opsworks_stack" "tf-acc" {
name = "tf-opsworks-acc"
region = "us-west-2"
service_role_arn = "%s"
default_instance_profile_arn = "%s"
default_availability_zone = "us-west-2a"
default_os = "Amazon Linux 2014.09"
default_root_device_type = "ebs"
custom_json = "{\"key\": \"value\"}"
configuration_manager_version = "11.10"
use_opsworks_security_groups = false
}
`
var testAccAWSOpsworksStackConfigNoVpcUpdate = `
resource "aws_opsworks_stack" "tf-acc" {
name = "tf-opsworks-acc"
region = "us-west-2"
service_role_arn = "%s"
default_instance_profile_arn = "%s"
default_availability_zone = "us-west-2a"
default_os = "Amazon Linux 2014.09"
default_root_device_type = "ebs"
custom_json = "{\"key\": \"value\"}"
configuration_manager_version = "11.10"
use_opsworks_security_groups = false
use_custom_cookbooks = true
manage_berkshelf = true
custom_cookbooks_source {
type = "git"
revision = "master"
url = "https://github.com/awslabs/opsworks-example-cookbooks.git"
}
}
`
func TestAccAwsOpsworksStackNoVpc(t *testing.T) {
opsiam := testAccAwsOpsworksStackIam{}
testAccAwsOpsworksStackPopulateIam(t, &opsiam)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsOpsworksStackDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(testAccAwsOpsworksStackConfigNoVpcCreate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
Check: testAccAwsOpsworksStackCheckResourceAttrsCreate,
},
resource.TestStep{
Config: fmt.Sprintf(testAccAWSOpsworksStackConfigNoVpcUpdate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
Check: testAccAwsOpsworksStackCheckResourceAttrsUpdate,
},
},
})
}
////////////////////////////
//// Tests for the VPC case
////////////////////////////
var testAccAwsOpsworksStackConfigVpcCreate = `
resource "aws_vpc" "tf-acc" {
cidr_block = "10.3.5.0/24"
}
resource "aws_subnet" "tf-acc" {
vpc_id = "${aws_vpc.tf-acc.id}"
cidr_block = "${aws_vpc.tf-acc.cidr_block}"
availability_zone = "us-west-2a"
}
resource "aws_opsworks_stack" "tf-acc" {
name = "tf-opsworks-acc"
region = "us-west-2"
vpc_id = "${aws_vpc.tf-acc.id}"
default_subnet_id = "${aws_subnet.tf-acc.id}"
service_role_arn = "%s"
default_instance_profile_arn = "%s"
default_os = "Amazon Linux 2014.09"
default_root_device_type = "ebs"
custom_json = "{\"key\": \"value\"}"
configuration_manager_version = "11.10"
use_opsworks_security_groups = false
}
`
var testAccAWSOpsworksStackConfigVpcUpdate = `
resource "aws_vpc" "tf-acc" {
cidr_block = "10.3.5.0/24"
}
resource "aws_subnet" "tf-acc" {
vpc_id = "${aws_vpc.tf-acc.id}"
cidr_block = "${aws_vpc.tf-acc.cidr_block}"
availability_zone = "us-west-2a"
}
resource "aws_opsworks_stack" "tf-acc" {
name = "tf-opsworks-acc"
region = "us-west-2"
vpc_id = "${aws_vpc.tf-acc.id}"
default_subnet_id = "${aws_subnet.tf-acc.id}"
service_role_arn = "%s"
default_instance_profile_arn = "%s"
default_os = "Amazon Linux 2014.09"
default_root_device_type = "ebs"
custom_json = "{\"key\": \"value\"}"
configuration_manager_version = "11.10"
use_opsworks_security_groups = false
use_custom_cookbooks = true
manage_berkshelf = true
custom_cookbooks_source {
type = "git"
revision = "master"
url = "https://github.com/awslabs/opsworks-example-cookbooks.git"
}
}
`
func TestAccAwsOpsworksStackVpc(t *testing.T) {
opsiam := testAccAwsOpsworksStackIam{}
testAccAwsOpsworksStackPopulateIam(t, &opsiam)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsOpsworksStackDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(testAccAwsOpsworksStackConfigVpcCreate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
Check: testAccAwsOpsworksStackCheckResourceAttrsCreate,
},
resource.TestStep{
Config: fmt.Sprintf(testAccAWSOpsworksStackConfigVpcUpdate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
Check: resource.ComposeTestCheckFunc(
testAccAwsOpsworksStackCheckResourceAttrsUpdate,
testAccAwsOpsworksCheckVpc,
),
},
},
})
}
////////////////////////////
//// Checkers and Utilities
////////////////////////////
var testAccAwsOpsworksStackCheckResourceAttrsCreate = resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"name",
"tf-opsworks-acc",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"default_availability_zone",
"us-west-2a",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"default_os",
"Amazon Linux 2014.09",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"default_root_device_type",
"ebs",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"custom_json",
`{"key": "value"}`,
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"configuration_manager_version",
"11.10",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"use_opsworks_security_groups",
"false",
),
)
var testAccAwsOpsworksStackCheckResourceAttrsUpdate = resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"name",
"tf-opsworks-acc",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"default_availability_zone",
"us-west-2a",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"default_os",
"Amazon Linux 2014.09",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"default_root_device_type",
"ebs",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"custom_json",
`{"key": "value"}`,
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"configuration_manager_version",
"11.10",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"use_opsworks_security_groups",
"false",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"use_custom_cookbooks",
"true",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"manage_berkshelf",
"true",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"custom_cookbooks_source.0.type",
"git",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"custom_cookbooks_source.0.revision",
"master",
),
resource.TestCheckResourceAttr(
"aws_opsworks_stack.tf-acc",
"custom_cookbooks_source.0.url",
"https://github.com/awslabs/opsworks-example-cookbooks.git",
),
)
func testAccAwsOpsworksCheckVpc(s *terraform.State) error {
rs, ok := s.RootModule().Resources["aws_opsworks_stack.tf-acc"]
if !ok {
return fmt.Errorf("Not found: %s", "aws_opsworks_stack.tf-acc")
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
p := rs.Primary
opsworksconn := testAccProvider.Meta().(*AWSClient).opsworksconn
describeOpts := &opsworks.DescribeStacksInput{
StackIds: []*string{aws.String(p.ID)},
}
resp, err := opsworksconn.DescribeStacks(describeOpts)
if err != nil {
return err
}
if len(resp.Stacks) == 0 {
return fmt.Errorf("No stack %s not found", p.ID)
}
if p.Attributes["vpc_id"] != *resp.Stacks[0].VpcId {
return fmt.Errorf("VPCID Got %s, expected %s", *resp.Stacks[0].VpcId, p.Attributes["vpc_id"])
}
if p.Attributes["default_subnet_id"] != *resp.Stacks[0].DefaultSubnetId {
return fmt.Errorf("VPCID Got %s, expected %s", *resp.Stacks[0].DefaultSubnetId, p.Attributes["default_subnet_id"])
}
return nil
}
func testAccCheckAwsOpsworksStackDestroy(s *terraform.State) error {
if len(s.RootModule().Resources) > 0 {
return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources)
}
return nil
}
// Holds the two IAM object ARNs used in stack objects we'll create.
type testAccAwsOpsworksStackIam struct {
ServiceRoleArn string
InstanceProfileArn string
}
func testAccAwsOpsworksStackPopulateIam(t *testing.T, opsiam *testAccAwsOpsworksStackIam) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccInstanceConfig_pre, // noop
Check: testAccCheckAwsOpsworksEnsureIam(t, opsiam),
},
},
})
}
func testAccCheckAwsOpsworksEnsureIam(t *testing.T, opsiam *testAccAwsOpsworksStackIam) func(*terraform.State) error {
return func(_ *terraform.State) error {
iamconn := testAccProvider.Meta().(*AWSClient).iamconn
serviceRoleOpts := &iam.GetRoleInput{
RoleName: aws.String("aws-opsworks-service-role"),
}
respServiceRole, err := iamconn.GetRole(serviceRoleOpts)
if err != nil {
return err
}
instanceProfileOpts := &iam.GetInstanceProfileInput{
InstanceProfileName: aws.String("aws-opsworks-ec2-role"),
}
respInstanceProfile, err := iamconn.GetInstanceProfile(instanceProfileOpts)
if err != nil {
return err
}
opsiam.ServiceRoleArn = *respServiceRole.Role.Arn
opsiam.InstanceProfileArn = *respInstanceProfile.InstanceProfile.Arn
t.Logf("[DEBUG] ServiceRoleARN for OpsWorks: %s", opsiam.ServiceRoleArn)
t.Logf("[DEBUG] Instance Profile ARN for OpsWorks: %s", opsiam.InstanceProfileArn)
return nil
}
}

View File

@ -0,0 +1,16 @@
package aws
import (
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsOpsworksStaticWebLayer() *schema.Resource {
layerType := &opsworksLayerType{
TypeName: "web",
DefaultLayerName: "Static Web Server",
Attributes: map[string]*opsworksLayerTypeAttribute{},
}
return layerType.SchemaResource()
}

View File

@ -0,0 +1,65 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_custom_layer"
sidebar_current: "docs-aws-resource-opsworks-custom-layer"
description: |-
Provides an OpsWorks custom layer resource.
---
# aws\_opsworks\_custom\_layer
Provides an OpsWorks custom layer resource.
## Example Usage
```
resource "aws_opsworks_custom_layer" "custlayer" {
name = "My Awesome Custom Layer"
short_name = "awesome"
stack_id = "${aws_opsworks_stack.main.id}"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) A human-readable name for the layer.
* `short_name` - (Required) A short, machine-readable name for the layer, which will be used to identify it in the Chef node JSON.
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -0,0 +1,66 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_ganglia_layer"
sidebar_current: "docs-aws-resource-opsworks-ganglia-layer"
description: |-
Provides an OpsWorks Ganglia layer resource.
---
# aws\_opsworks\_ganglia\_layer
Provides an OpsWorks Ganglia layer resource.
## Example Usage
```
resource "aws_opsworks_ganglia_layer" "monitor" {
stack_id = "${aws_opsworks_stack.main.id}"
password = "foobarbaz"
}
```
## Argument Reference
The following arguments are supported:
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `password` - (Required) The password to use for Ganglia.
* `name` - (Optional) A human-readable name for the layer.
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `url` - (Optional) The URL path to use for Ganglia. Defaults to "/ganglia".
* `username` - (Optiona) The username to use for Ganglia. Defaults to "opsworks".
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -0,0 +1,69 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_haproxy_layer"
sidebar_current: "docs-aws-resource-opsworks-haproxy-layer"
description: |-
Provides an OpsWorks HAProxy layer resource.
---
# aws\_opsworks\_haproxy\_layer
Provides an OpsWorks haproxy layer resource.
## Example Usage
```
resource "aws_opsworks_haproxy_layer" "lb" {
stack_id = "${aws_opsworks_stack.main.id}"
stats_password = "foobarbaz"
}
```
## Argument Reference
The following arguments are supported:
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `stats_password` - (Required) The password to use for HAProxy stats.
* `name` - (Optional) A human-readable name for the layer.
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `healthcheck_method` - (Optional) HTTP method to use for instance healthchecks. Defaults to "OPTIONS".
* `healthcheck_url` - (Optional) URL path to use for instance healthchecks. Defaults to "/".
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `stats_enabled` - (Optional) Whether to enable HAProxy stats.
* `stats_url` - (Optional) The HAProxy stats URL. Defaults to "/haproxy?stats".
* `stats_user` - (Optional) The username for HAProxy stats. Defaults to "opsworks".
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -0,0 +1,67 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_java_app_layer"
sidebar_current: "docs-aws-resource-opsworks-java-app-layer"
description: |-
Provides an OpsWorks Java application layer resource.
---
# aws\_opsworks\_java\_app\_layer
Provides an OpsWorks Java application layer resource.
## Example Usage
```
resource "aws_opsworks_java_app_layer" "app" {
stack_id = "${aws_opsworks_stack.main.id}"
}
```
## Argument Reference
The following arguments are supported:
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `name` - (Optional) A human-readable name for the layer.
* `app_server` - (Optional) Keyword for the application container to use. Defaults to "tomcat".
* `app_server_version` - (Optional) Version of the selected application container to use. Defaults to "7".
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `jvm_type` - (Optional) Keyword for the type of JVM to use. Defaults to `openjdk`.
* `jvm_options` - (Optional) Options to set for the JVM.
* `jvm_version` - (Optional) Version of JVM to use. Defaults to "7".
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -0,0 +1,63 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_memcached_layer"
sidebar_current: "docs-aws-resource-opsworks-memcached-layer"
description: |-
Provides an OpsWorks memcached layer resource.
---
# aws\_opsworks\_memcached\_layer
Provides an OpsWorks memcached layer resource.
## Example Usage
```
resource "aws_opsworks_memcached_layer" "cache" {
stack_id = "${aws_opsworks_stack.main.id}"
}
```
## Argument Reference
The following arguments are supported:
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `name` - (Optional) A human-readable name for the layer.
* `allocated_memory` - (Optional) Amount of memory to allocate for the cache on each instance, in megabytes. Defaults to 512MB.
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -0,0 +1,64 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_mysql_layer"
sidebar_current: "docs-aws-resource-opsworks-mysql-layer"
description: |-
Provides an OpsWorks MySQL layer resource.
---
# aws\_opsworks\_mysql\_layer
Provides an OpsWorks MySQL layer resource.
## Example Usage
```
resource "aws_opsworks_mysql_layer" "db" {
stack_id = "${aws_opsworks_stack.main.id}"
}
```
## Argument Reference
The following arguments are supported:
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `name` - (Optional) A human-readable name for the layer.
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `root_password` - (Optional) Root password to use for MySQL.
* `root_password_on_all_instances` - (Optional) Whether to set the root user password to all instances in the stack so they can access the instances in this layer.
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -0,0 +1,63 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_nodejs_app_layer"
sidebar_current: "docs-aws-resource-opsworks-nodejs-app-layer"
description: |-
Provides an OpsWorks NodeJS application layer resource.
---
# aws\_opsworks\_nodejs\_app\_layer
Provides an OpsWorks NodeJS application layer resource.
## Example Usage
```
resource "aws_opsworks_nodejs_app_layer" "app" {
stack_id = "${aws_opsworks_stack.main.id}"
}
```
## Argument Reference
The following arguments are supported:
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `name` - (Optional) A human-readable name for the layer.
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `nodejs_version` - (Optional) The version of NodeJS to use. Defaults to "0.10.38".
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -0,0 +1,62 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_php_app_layer"
sidebar_current: "docs-aws-resource-opsworks-php-app-layer"
description: |-
Provides an OpsWorks PHP application layer resource.
---
# aws\_opsworks\_php\_app\_layer
Provides an OpsWorks PHP application layer resource.
## Example Usage
```
resource "aws_opsworks_php_app_layer" "app" {
stack_id = "${aws_opsworks_stack.main.id}"
}
```
## Argument Reference
The following arguments are supported:
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `name` - (Optional) A human-readable name for the layer.
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -0,0 +1,68 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_rails_app_layer"
sidebar_current: "docs-aws-resource-opsworks-rails-app-layer"
description: |-
Provides an OpsWorks Ruby on Rails application layer resource.
---
# aws\_opsworks\_rails\_app\_layer
Provides an OpsWorks Ruby on Rails application layer resource.
## Example Usage
```
resource "aws_opsworks_rails_app_layer" "app" {
stack_id = "${aws_opsworks_stack.main.id}"
}
```
## Argument Reference
The following arguments are supported:
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `name` - (Optional) A human-readable name for the layer.
* `app_server` - (Optional) Keyword for the app server to use. Defaults to "apache_passenger".
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `bundler_version` - (Optional) When OpsWorks is managing Bundler, which version to use. Defaults to "1.5.3".
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `manage_bundler` - (Optional) Whether OpsWorks should manage bundler. On by default.
* `passenger_version` - (Optional) The version of Passenger to use. Defaults to "4.0.46".
* `ruby_version` - (Optional) The version of Ruby to use. Defaults to "2.0.0".
* `rubygems_version` - (Optional) The version of RubyGems to use. Defaults to "2.2.2".
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -0,0 +1,68 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_stack"
sidebar_current: "docs-aws-resource-opsworks-stack"
description: |-
Provides an OpsWorks stack resource.
---
# aws\_opsworks\_stack
Provides an OpsWorks stack resource.
## Example Usage
```
resource "aws_opsworks_stack" "main" {
name = "awesome-stack"
region = "us-west-1"
service_role_arn = "${aws_iam_role.opsworks.arn}"
default_instance_profile_arn = "${aws_iam_instance_profile.opsworks.arn}"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the stack.
* `region` - (Required) The name of the region where the stack will exist.
* `service_role_arn` - (Required) The ARN of an IAM role that the OpsWorks service will act as.
* `default_instance_profile_arn` - (Required) The ARN of an IAM Instance Profile that created instances
will have by default.
* `berkshelf_version` - (Optional) If `manage_berkshelf` is enabled, the version of Berkshelf to use.
* `color` - (Optional) Color to paint next to the stack's resources in the OpsWorks console.
* `default_availability_zone` - (Optional) Name of the availability zone where instances will be created
by default. This is required unless you set `vpc_id`.
* `configuration_manager_name` - (Optional) Name of the configuration manager to use. Defaults to "Chef".
* `configuration_manager_version` - (Optional) Version of the configuratino manager to use. Defaults to "11.4".
* `custom_cookbooks_source` - (Optional) When `use_custom_cookbooks` is set, provide this sub-object as
described below.
* `default_os` - (Optional) Name of OS that will be installed on instances by default.
* `default_root_device_type` - (Optional) Name of the type of root device instances will have by default.
* `default_ssh_key_name` - (Optional) Name of the SSH keypair that instances will have by default.
* `default_subnet_id` - (Optional) Id of the subnet in which instances will be created by default. Mandatory
if `vpc_id` is set, and forbidden if it isn't.
* `hostname_theme` - (Optional) Keyword representing the naming scheme that will be used for instance hostnames
within this stack.
* `manage_berkshelf` - (Optional) Boolean value controlling whether Opsworks will run Berkshelf for this stack.
* `use_custom_cookbooks` - (Optional) Boolean value controlling whether the custom cookbook settings are
enabled.
* `use_opsworks_security_groups` - (Optional) Boolean value controlling whether the standard OpsWorks
security groups apply to created instances.
* `vpc_id` - (Optional) The id of the VPC that this stack belongs to.
The `custom_cookbooks_source` block supports the following arguments:
* `type` - (Required) The type of source to use. For example, "archive".
* `url` - (Required) The URL where the cookbooks resource can be found.
* `username` - (Optional) Username to use when authenticating to the source.
* `password` - (Optional) Password to use when authenticating to the source.
* `ssh_key` - (Optional) SSH key to use when authenticating to the source.
* `revision` - (Optional) For sources that are version-aware, the revision to use.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the stack.

View File

@ -0,0 +1,62 @@
---
layout: "aws"
page_title: "AWS: aws_opsworks_static_web_layer"
sidebar_current: "docs-aws-resource-opsworks-static-web-layer"
description: |-
Provides an OpsWorks static web server layer resource.
---
# aws\_opsworks\_static\_web\_layer
Provides an OpsWorks static web server layer resource.
## Example Usage
```
resource "aws_opsworks_static_web_layer" "web" {
stack_id = "${aws_opsworks_stack.main.id}"
}
```
## Argument Reference
The following arguments are supported:
* `stack_id` - (Required) The id of the stack the layer will belong to.
* `name` - (Optional) A human-readable name for the layer.
* `auto_assign_elastic_ips` - (Optional) Whether to automatically assign an elastic IP address to the layer's instances.
* `auto_assign_public_ips` - (Optional) For stacks belonging to a VPC, whether to automatically assign a public IP address to each of the layer's instances.
* `custom_instance_profile_arn` - (Optional) The ARN of an IAM profile that will be used for the layer's instances.
* `custom_security_group_ids` - (Optional) Ids for a set of security groups to apply to the layer's instances.
* `auto_healing` - (Optional) Whether to enable auto-healing for the layer.
* `install_updates_on_boot` - (Optional) Whether to install OS and package updates on each instance when it boots.
* `instance_shutdown_timeout` - (Optional) The time, in seconds, that OpsWorks will wait for Chef to complete after triggering the Shutdown event.
* `drain_elb_on_shutdown` - (Optional) Whether to enable Elastic Load Balancing connection draining.
* `system_packages` - (Optional) Names of a set of system packages to install on the layer's instances.
* `use_ebs_optimized_instances` - (Optional) Whether to use EBS-optimized instances.
* `ebs_volume` - (Optional) `ebs_volume` blocks, as described below, will each create an EBS volume and connect it to the layer's instances.
The following extra optional arguments, all lists of Chef recipe names, allow
custom Chef recipes to be applied to layer instances at the five different
lifecycle events, if custom cookbooks are enabled on the layer's stack:
* `custom_configure_recipes`
* `custom_deploy_recipes`
* `custom_setup_recipes`
* `custom_shutdown_recipes`
* `custom_undeploy_recipes`
An `ebs_volume` block supports the following arguments:
* `mount_point` - (Required) The path to mount the EBS volume on the layer's instances.
* `size` - (Required) The size of the volume in gigabytes.
* `number_of_disks` - (Required) The number of disks to use for the EBS volume.
* `raid_level` - (Required) The RAID level to use for the volume.
* `type` - (Optional) The type of volume to create. This may be `standard` (the default), `io1` or `gp2`.
* `iops` - (Optional) For PIOPS volumes, the IOPS per disk.
## Attributes Reference
The following attributes are exported:
* `id` - The id of the layer.

View File

@ -255,6 +255,58 @@
</li>
<li<%= sidebar_current(/^docs-aws-resource-opsworks/) %>>
<a href="#">OpsWorks Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-opsworks-custom-layer") %>>
<a href="/docs/providers/aws/r/opsworks_custom_layer.html">aws_opsworks_custom_layer</a>
</li>
<li<%= sidebar_current("docs-aws-resource-opsworks-ganglia-layer") %>>
<a href="/docs/providers/aws/r/opsworks_ganglia_layer.html">aws_opsworks_ganglia_layer</a>
</li>
<li<%= sidebar_current("docs-aws-resource-opsworks-haproxy-layer") %>>
<a href="/docs/providers/aws/r/opsworks_haproxy_layer.html">aws_opsworks_haproxy_layer</a>
</li>
<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>
</li>
<li<%= sidebar_current("docs-aws-resource-opsworks-memcached-layer") %>>
<a href="/docs/providers/aws/r/opsworks_memcached_layer.html">aws_opsworks_memcached_layer</a>
</li>
<li<%= sidebar_current("docs-aws-resource-opsworks-mysql-layer") %>>
<a href="/docs/providers/aws/r/opsworks_mysql_layer.html">aws_opsworks_mysql_layer</a>
</li>
<li<%= sidebar_current("docs-aws-resource-opsworks-nodejs-app-layer") %>>
<a href="/docs/providers/aws/r/opsworks_nodejs_app_layer.html">aws_opsworks_nodejs_app_layer</a>
</li>
<li<%= sidebar_current("docs-aws-resource-opsworks-php-app-layer") %>>
<a href="/docs/providers/aws/r/opsworks_php_app_layer.html">aws_opsworks_php_app_layer</a>
</li>
<li<%= sidebar_current("docs-aws-resource-opsworks-rails-app-layer") %>>
<a href="/docs/providers/aws/r/opsworks_rails_app_layer.html">aws_opsworks_rails_app_layer</a>
</li>
<li<%= sidebar_current("docs-aws-resource-opsworks-stack") %>>
<a href="/docs/providers/aws/r/opsworks_stack.html">aws_opsworks_stack</a>
</li>
<li<%= sidebar_current("docs-aws-resource-opsworks-static-web-layer") %>>
<a href="/docs/providers/aws/r/opsworks_static_web_layer.html">aws_opsworks_static_web_layer</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-db/) %>>
<a href="#">RDS Resources</a>
<ul class="nav nav-visible">