AWS Application AutoScaling
Initial work on two new resource types: * `aws_appautoscaling_target` * `aws_appautoscaling_policy` Fix acc tests
This commit is contained in:
parent
a61687add1
commit
cc912c39e5
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/apigateway"
|
||||
"github.com/aws/aws-sdk-go/service/applicationautoscaling"
|
||||
"github.com/aws/aws-sdk-go/service/autoscaling"
|
||||
"github.com/aws/aws-sdk-go/service/cloudformation"
|
||||
"github.com/aws/aws-sdk-go/service/cloudfront"
|
||||
|
@ -94,6 +95,7 @@ type AWSClient struct {
|
|||
emrconn *emr.EMR
|
||||
esconn *elasticsearch.ElasticsearchService
|
||||
apigateway *apigateway.APIGateway
|
||||
appautoscalingconn *applicationautoscaling.ApplicationAutoScaling
|
||||
autoscalingconn *autoscaling.AutoScaling
|
||||
s3conn *s3.S3
|
||||
sesConn *ses.SES
|
||||
|
@ -213,6 +215,7 @@ func (c *Config) Client() (interface{}, error) {
|
|||
}
|
||||
|
||||
client.apigateway = apigateway.New(sess)
|
||||
client.appautoscalingconn = applicationautoscaling.New(sess)
|
||||
client.autoscalingconn = autoscaling.New(sess)
|
||||
client.cfconn = cloudformation.New(sess)
|
||||
client.cloudfrontconn = cloudfront.New(sess)
|
||||
|
|
|
@ -135,6 +135,8 @@ func Provider() terraform.ResourceProvider {
|
|||
"aws_api_gateway_resource": resourceAwsApiGatewayResource(),
|
||||
"aws_api_gateway_rest_api": resourceAwsApiGatewayRestApi(),
|
||||
"aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(),
|
||||
"aws_appautoscaling_target": resourceAwsAppautoscalingTarget(),
|
||||
"aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(),
|
||||
"aws_autoscaling_group": resourceAwsAutoscalingGroup(),
|
||||
"aws_autoscaling_notification": resourceAwsAutoscalingNotification(),
|
||||
"aws_autoscaling_policy": resourceAwsAutoscalingPolicy(),
|
||||
|
|
|
@ -0,0 +1,331 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/applicationautoscaling"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func resourceAwsAppautoscalingPolicy() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceAwsAppautoscalingPolicyCreate,
|
||||
Read: resourceAwsAppautoscalingPolicyRead,
|
||||
Update: resourceAwsAppautoscalingPolicyUpdate,
|
||||
Delete: resourceAwsAppautoscalingPolicyDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||
// https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1862-L1873
|
||||
value := v.(string)
|
||||
if len(value) > 255 {
|
||||
errors = append(errors, fmt.Errorf("q cannot be longer than 255 characters", k))
|
||||
}
|
||||
return
|
||||
},
|
||||
},
|
||||
"arn": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"policy_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "StepScaling",
|
||||
},
|
||||
"resource_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"scalable_dimension": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "ecs:service:DesiredCount",
|
||||
ForceNew: true,
|
||||
},
|
||||
"service_namespace": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "ecs",
|
||||
ForceNew: true,
|
||||
},
|
||||
"adjustment_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"cooldown": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
"metric_aggregation_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"min_adjustment_magnitude": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
},
|
||||
"alarms": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
"step_adjustment": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"metric_interval_lower_bound": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"metric_interval_upper_bound": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"scaling_adjustment": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Set: resourceAwsAppautoscalingAdjustmentHash,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceAwsAppautoscalingPolicyCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).appautoscalingconn
|
||||
|
||||
params, err := getAwsAppautoscalingPutScalingPolicyInput(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] ApplicationAutoScaling PutScalingPolicy: %#v", params)
|
||||
resp, err := conn.PutScalingPolicy(¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error putting scaling policy: %s", err)
|
||||
}
|
||||
|
||||
d.Set("arn", resp.PolicyARN)
|
||||
d.SetId(d.Get("name").(string))
|
||||
log.Printf("[INFO] ApplicationAutoScaling scaling PolicyARN: %s", d.Get("arn").(string))
|
||||
|
||||
return resourceAwsAppautoscalingPolicyRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceAwsAppautoscalingPolicyRead(d *schema.ResourceData, meta interface{}) error {
|
||||
p, err := getAwsAppautoscalingPolicy(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p == nil {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Read ApplicationAutoScaling policy: %s, SP: %s, Obj: %s", d.Get("name"), d.Get("name"), p)
|
||||
|
||||
d.Set("arn", p.PolicyARN)
|
||||
d.Set("name", p.PolicyName)
|
||||
d.Set("policy_type", p.PolicyType)
|
||||
d.Set("resource_id", p.ResourceId)
|
||||
d.Set("scalable_dimension", p.ScalableDimension)
|
||||
d.Set("service_namespace", p.ServiceNamespace)
|
||||
d.Set("alarms", p.Alarms)
|
||||
d.Set("step_scaling_policy_configuration", p.StepScalingPolicyConfiguration)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceAwsAppautoscalingPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).appautoscalingconn
|
||||
|
||||
params, inputErr := getAwsAppautoscalingPutScalingPolicyInput(d)
|
||||
if inputErr != nil {
|
||||
return inputErr
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Application Autoscaling Update Scaling Policy: %#v", params)
|
||||
_, err := conn.PutScalingPolicy(¶ms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return resourceAwsAppautoscalingPolicyRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceAwsAppautoscalingPolicyDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).appautoscalingconn
|
||||
p, err := getAwsAppautoscalingPolicy(d, meta)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting policy: %s", err)
|
||||
}
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
params := applicationautoscaling.DeleteScalingPolicyInput{
|
||||
PolicyName: aws.String(d.Get("name").(string)),
|
||||
ResourceId: aws.String(d.Get("resource_id").(string)),
|
||||
ScalableDimension: aws.String(d.Get("scalable_dimension").(string)),
|
||||
ServiceNamespace: aws.String(d.Get("service_namespace").(string)),
|
||||
}
|
||||
log.Printf("[DEBUG] Deleting Application AutoScaling Policy opts: %#v", params)
|
||||
if _, err := conn.DeleteScalingPolicy(¶ms); err != nil {
|
||||
return fmt.Errorf("Application AutoScaling Policy: %s", err)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Takes the result of flatmap.Expand for an array of step adjustments and
|
||||
// returns a []*applicationautoscaling.StepAdjustment.
|
||||
func expandAppautoscalingStepAdjustments(configured []interface{}) ([]*applicationautoscaling.StepAdjustment, error) {
|
||||
var adjustments []*applicationautoscaling.StepAdjustment
|
||||
|
||||
// Loop over our configured step adjustments and create an array
|
||||
// of aws-sdk-go compatible objects. We're forced to convert strings
|
||||
// to floats here because there's no way to detect whether or not
|
||||
// an uninitialized, optional schema element is "0.0" deliberately.
|
||||
// With strings, we can test for "", which is definitely an empty
|
||||
// struct value.
|
||||
for _, raw := range configured {
|
||||
data := raw.(map[string]interface{})
|
||||
a := &applicationautoscaling.StepAdjustment{
|
||||
ScalingAdjustment: aws.Int64(int64(data["scaling_adjustment"].(int))),
|
||||
}
|
||||
if data["metric_interval_lower_bound"] != "" {
|
||||
bound := data["metric_interval_lower_bound"]
|
||||
switch bound := bound.(type) {
|
||||
case string:
|
||||
f, err := strconv.ParseFloat(bound, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"metric_interval_lower_bound must be a float value represented as a string")
|
||||
}
|
||||
a.MetricIntervalLowerBound = aws.Float64(f)
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"metric_interval_lower_bound isn't a string. This is a bug. Please file an issue.")
|
||||
}
|
||||
}
|
||||
if data["metric_interval_upper_bound"] != "" {
|
||||
bound := data["metric_interval_upper_bound"]
|
||||
switch bound := bound.(type) {
|
||||
case string:
|
||||
f, err := strconv.ParseFloat(bound, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"metric_interval_upper_bound must be a float value represented as a string")
|
||||
}
|
||||
a.MetricIntervalUpperBound = aws.Float64(f)
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"metric_interval_upper_bound isn't a string. This is a bug. Please file an issue.")
|
||||
}
|
||||
}
|
||||
adjustments = append(adjustments, a)
|
||||
}
|
||||
|
||||
return adjustments, nil
|
||||
}
|
||||
|
||||
func getAwsAppautoscalingPutScalingPolicyInput(d *schema.ResourceData) (applicationautoscaling.PutScalingPolicyInput, error) {
|
||||
var params = applicationautoscaling.PutScalingPolicyInput{
|
||||
PolicyName: aws.String(d.Get("name").(string)),
|
||||
ResourceId: aws.String(d.Get("resource_id").(string)),
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("policy_type"); ok {
|
||||
params.PolicyType = aws.String(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("service_namespace"); ok {
|
||||
params.ServiceNamespace = aws.String(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("policy_type"); ok {
|
||||
params.PolicyType = aws.String(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("scalable_dimension"); ok {
|
||||
params.ScalableDimension = aws.String(v.(string))
|
||||
}
|
||||
|
||||
var adjustmentSteps []*applicationautoscaling.StepAdjustment
|
||||
if v, ok := d.GetOk("step_adjustment"); ok {
|
||||
steps, err := expandAppautoscalingStepAdjustments(v.(*schema.Set).List())
|
||||
if err != nil {
|
||||
return params, fmt.Errorf("metric_interval_lower_bound and metric_interval_upper_bound must be strings!")
|
||||
}
|
||||
adjustmentSteps = steps
|
||||
}
|
||||
|
||||
// build StepScalingPolicyConfiguration
|
||||
params.StepScalingPolicyConfiguration = &applicationautoscaling.StepScalingPolicyConfiguration{
|
||||
AdjustmentType: aws.String(d.Get("adjustment_type").(string)),
|
||||
Cooldown: aws.Int64(int64(d.Get("cooldown").(int))),
|
||||
MetricAggregationType: aws.String(d.Get("metric_aggregation_type").(string)),
|
||||
StepAdjustments: adjustmentSteps,
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("min_adjustment_magnitude"); ok {
|
||||
params.StepScalingPolicyConfiguration.MinAdjustmentMagnitude = aws.Int64(int64(v.(int)))
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func getAwsAppautoscalingPolicy(d *schema.ResourceData, meta interface{}) (*applicationautoscaling.ScalingPolicy, error) {
|
||||
conn := meta.(*AWSClient).appautoscalingconn
|
||||
|
||||
params := applicationautoscaling.DescribeScalingPoliciesInput{
|
||||
PolicyNames: []*string{aws.String(d.Get("name").(string))},
|
||||
ServiceNamespace: aws.String(d.Get("service_namespace").(string)),
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Application AutoScaling Policy Describe Params: %#v", params)
|
||||
resp, err := conn.DescribeScalingPolicies(¶ms)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error retrieving scaling policies: %s", err)
|
||||
}
|
||||
|
||||
// find scaling policy
|
||||
name := d.Get("name")
|
||||
for idx, sp := range resp.ScalingPolicies {
|
||||
if *sp.PolicyName == name {
|
||||
return resp.ScalingPolicies[idx], nil
|
||||
}
|
||||
}
|
||||
|
||||
// policy not found
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func resourceAwsAppautoscalingAdjustmentHash(v interface{}) int {
|
||||
var buf bytes.Buffer
|
||||
m := v.(map[string]interface{})
|
||||
if v, ok := m["metric_interval_lower_bound"]; ok {
|
||||
buf.WriteString(fmt.Sprintf("%f-", v))
|
||||
}
|
||||
if v, ok := m["metric_interval_upper_bound"]; ok {
|
||||
buf.WriteString(fmt.Sprintf("%f-", v))
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%d-", m["scaling_adjustment"].(int)))
|
||||
|
||||
return hashcode.String(buf.String())
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/applicationautoscaling"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccAWSAppautoscalingPolicy_basic(t *testing.T) {
|
||||
var policy applicationautoscaling.ScalingPolicy
|
||||
var awsAccountId = os.Getenv("AWS_ACCOUNT_ID")
|
||||
|
||||
randClusterName := fmt.Sprintf("cluster-%s", acctest.RandString(10))
|
||||
// randResourceId := fmt.Sprintf("service/%s/%s", randClusterName, acctest.RandString(10))
|
||||
randPolicyName := fmt.Sprintf("terraform-test-foobar-%s", acctest.RandString(5))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSAppautoscalingPolicyDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSAppautoscalingPolicyConfig(randClusterName, randPolicyName, awsAccountId),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAppautoscalingPolicyExists("aws_appautoscaling_policy.foobar_simple", &policy),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "adjustment_type", "ChangeInCapacity"),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "policy_type", "StepScaling"),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "cooldown", "60"),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "name", randPolicyName),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "resource_id", fmt.Sprintf("service/%s/foobar", randClusterName)),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "service_namespace", "ecs"),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "scalable_dimension", "ecs:service:DesiredCount"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckAWSAppautoscalingPolicyExists(n string, policy *applicationautoscaling.ScalingPolicy) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn
|
||||
params := &applicationautoscaling.DescribeScalingPoliciesInput{
|
||||
ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
|
||||
PolicyNames: []*string{aws.String(rs.Primary.ID)},
|
||||
}
|
||||
resp, err := conn.DescribeScalingPolicies(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.ScalingPolicies) == 0 {
|
||||
return fmt.Errorf("ScalingPolicy %s not found", rs.Primary.ID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSAppautoscalingPolicyDestroy(s *terraform.State) error {
|
||||
conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
params := applicationautoscaling.DescribeScalingPoliciesInput{
|
||||
ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
|
||||
PolicyNames: []*string{aws.String(rs.Primary.ID)},
|
||||
}
|
||||
|
||||
resp, err := conn.DescribeScalingPolicies(¶ms)
|
||||
|
||||
if err == nil {
|
||||
if len(resp.ScalingPolicies) != 0 &&
|
||||
*resp.ScalingPolicies[0].PolicyName == rs.Primary.ID {
|
||||
return fmt.Errorf("Application autoscaling policy still exists: %s", rs.Primary.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccAWSAppautoscalingPolicyConfig(
|
||||
randClusterName string,
|
||||
randPolicyName string,
|
||||
awsAccountId string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_ecs_cluster" "foo" {
|
||||
name = "%s"
|
||||
}
|
||||
|
||||
resource "aws_ecs_task_definition" "task" {
|
||||
family = "foobar"
|
||||
container_definitions = <<EOF
|
||||
[
|
||||
{
|
||||
"name": "busybox",
|
||||
"image": "busybox:latest",
|
||||
"cpu": 10,
|
||||
"memory": 128,
|
||||
"essential": true
|
||||
}
|
||||
]
|
||||
EOF
|
||||
}
|
||||
|
||||
resource "aws_ecs_service" "service" {
|
||||
name = "foobar"
|
||||
cluster = "${aws_ecs_cluster.foo.id}"
|
||||
task_definition = "${aws_ecs_task_definition.task.arn}"
|
||||
desired_count = 1
|
||||
deployment_maximum_percent = 200
|
||||
deployment_minimum_healthy_percent = 50
|
||||
}
|
||||
|
||||
resource "aws_appautoscaling_target" "tgt" {
|
||||
service_namespace = "ecs"
|
||||
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
|
||||
scalable_dimension = "ecs:service:DesiredCount"
|
||||
role_arn = "arn:aws:iam::%s:role/ecsAutoscaleRole"
|
||||
min_capacity = 1
|
||||
max_capacity = 4
|
||||
}
|
||||
|
||||
resource "aws_appautoscaling_policy" "foobar_simple" {
|
||||
name = "%s"
|
||||
service_namespace = "ecs"
|
||||
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
|
||||
scalable_dimension = "ecs:service:DesiredCount"
|
||||
adjustment_type = "ChangeInCapacity"
|
||||
cooldown = 60
|
||||
metric_aggregation_type = "Average"
|
||||
step_adjustment {
|
||||
metric_interval_lower_bound = 0
|
||||
scaling_adjustment = 1
|
||||
}
|
||||
depends_on = ["aws_appautoscaling_target.tgt"]
|
||||
}
|
||||
`, randClusterName, awsAccountId, randPolicyName)
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"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/applicationautoscaling"
|
||||
)
|
||||
|
||||
func resourceAwsAppautoscalingTarget() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceAwsAppautoscalingTargetCreate,
|
||||
Read: resourceAwsAppautoscalingTargetRead,
|
||||
Delete: resourceAwsAppautoscalingTargetDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||
// https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1862-L1873
|
||||
value := v.(string)
|
||||
if len(value) > 255 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q cannot be longer than 255 characters", k))
|
||||
}
|
||||
return
|
||||
},
|
||||
},
|
||||
"arn": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"max_capacity": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"min_capacity": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"resource_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"role_arn": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"scalable_dimension": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "ecs:service:DesiredCount",
|
||||
ForceNew: true,
|
||||
},
|
||||
"service_namespace": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "ecs",
|
||||
ForceNew: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceAwsAppautoscalingTargetCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).appautoscalingconn
|
||||
|
||||
var targetOpts applicationautoscaling.RegisterScalableTargetInput
|
||||
|
||||
targetOpts.MaxCapacity = aws.Int64(int64(d.Get("max_capacity").(int)))
|
||||
targetOpts.MinCapacity = aws.Int64(int64(d.Get("min_capacity").(int)))
|
||||
targetOpts.ResourceId = aws.String(d.Get("resource_id").(string))
|
||||
targetOpts.RoleARN = aws.String(d.Get("role_arn").(string))
|
||||
targetOpts.ScalableDimension = aws.String(d.Get("scalable_dimension").(string))
|
||||
targetOpts.ServiceNamespace = aws.String(d.Get("service_namespace").(string))
|
||||
|
||||
log.Printf("[DEBUG] Application autoscaling target create configuration %#v", targetOpts)
|
||||
_, err := conn.RegisterScalableTarget(&targetOpts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating application autoscaling target: %s", err)
|
||||
}
|
||||
|
||||
d.SetId(d.Get("resource_id").(string))
|
||||
log.Printf("[INFO] Application AutoScaling Target ID: %s", d.Id())
|
||||
|
||||
return resourceAwsAppautoscalingTargetRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceAwsAppautoscalingTargetRead(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).appautoscalingconn
|
||||
|
||||
t, err := getAwsAppautoscalingTarget(d, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t == nil {
|
||||
log.Printf("[INFO] Application AutoScaling Target %q not found", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
d.Set("max_capacity", t.MaxCapacity)
|
||||
d.Set("min_capacity", t.MinCapacity)
|
||||
d.Set("resource_id", t.ResourceId)
|
||||
d.Set("role_arn", t.RoleARN)
|
||||
d.Set("scalable_dimension", t.ScalableDimension)
|
||||
d.Set("service_namespace", t.ServiceNamespace)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Updating Target is not supported
|
||||
// func getAwsAppautoscalingTargetUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
// conn := meta.(*AWSClient).appautoscalingconn
|
||||
|
||||
// }
|
||||
|
||||
func resourceAwsAppautoscalingTargetDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).appautoscalingconn
|
||||
|
||||
t, err := getAwsAppautoscalingTarget(d, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t == nil {
|
||||
log.Printf("[INFO] Application AutoScaling Target %q not found", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Application AutoScaling Target destroy: %#v", d.Id())
|
||||
deleteOpts := applicationautoscaling.DeregisterScalableTargetInput{
|
||||
ResourceId: aws.String(d.Get("resource_id").(string)),
|
||||
ServiceNamespace: aws.String(d.Get("service_namespace").(string)),
|
||||
ScalableDimension: aws.String(d.Get("scalable_dimension").(string)),
|
||||
}
|
||||
|
||||
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
|
||||
if _, err := conn.DeregisterScalableTarget(&deleteOpts); err != nil {
|
||||
if awserr, ok := err.(awserr.Error); ok {
|
||||
// @TODO: We should do stuff here depending on the actual error returned
|
||||
return resource.RetryableError(awserr)
|
||||
}
|
||||
// Non recognized error, no retry.
|
||||
return resource.NonRetryableError(err)
|
||||
}
|
||||
// Successful delete
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return resource.Retry(5*time.Minute, func() *resource.RetryError {
|
||||
if t, _ = getAwsAppautoscalingTarget(d, conn); t != nil {
|
||||
return resource.RetryableError(
|
||||
fmt.Errorf("Application AutoScaling Target still exists"))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func getAwsAppautoscalingTarget(
|
||||
d *schema.ResourceData,
|
||||
conn *applicationautoscaling.ApplicationAutoScaling) (*applicationautoscaling.ScalableTarget, error) {
|
||||
|
||||
tgtName := d.Id()
|
||||
describeOpts := applicationautoscaling.DescribeScalableTargetsInput{
|
||||
ResourceIds: []*string{aws.String(tgtName)},
|
||||
ServiceNamespace: aws.String(d.Get("service_namespace").(string)),
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Application AutoScaling Target describe configuration: %#v", describeOpts)
|
||||
describeTargets, err := conn.DescribeScalableTargets(&describeOpts)
|
||||
if err != nil {
|
||||
// @TODO: We should probably send something else back if we're trying to access an unknown Resource ID
|
||||
// targetserr, ok := err.(awserr.Error)
|
||||
// if ok && targetserr.Code() == ""
|
||||
return nil, fmt.Errorf("Error retrieving Application AutoScaling Target: %s", err)
|
||||
}
|
||||
|
||||
for idx, tgt := range describeTargets.ScalableTargets {
|
||||
if *tgt.ResourceId == tgtName {
|
||||
return describeTargets.ScalableTargets[idx], nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/applicationautoscaling"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccAWSAppautoScalingTarget_basic(t *testing.T) {
|
||||
var target applicationautoscaling.ScalableTarget
|
||||
var awsAccountId = os.Getenv("AWS_ACCOUNT_ID")
|
||||
|
||||
randClusterName := fmt.Sprintf("cluster-%s", acctest.RandString(10))
|
||||
randResourceId := fmt.Sprintf("service/%s/%s", randClusterName, acctest.RandString(10))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_appautoscaling_target.bar",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSAppautoscalingTargetDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSAppautoscalingTargetConfig(randClusterName, randResourceId, awsAccountId),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAppautoscalingTargetExists("aws_appautoscaling_target.bar", &target),
|
||||
testAccCheckAWSAppautoscalingTargetAttributes(&target, randResourceId),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "service_namespace", "ecs"),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "resource_id", fmt.Sprintf("service/%s/foobar", randClusterName)),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "scalable_dimension", "ecs:service:DesiredCount"),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "min_capacity", "1"),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "max_capacity", "3"),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: testAccAWSAppautoscalingTargetConfigUpdate(randClusterName, randResourceId, awsAccountId),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAppautoscalingTargetExists("aws_appautoscaling_target.bar", &target),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "min_capacity", "3"),
|
||||
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "max_capacity", "6"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckAWSAppautoscalingTargetDestroy(s *terraform.State) error {
|
||||
conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "aws_appautoscaling_target" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to find the target
|
||||
describeTargets, err := conn.DescribeScalableTargets(
|
||||
&applicationautoscaling.DescribeScalableTargetsInput{
|
||||
ResourceIds: []*string{aws.String(rs.Primary.ID)},
|
||||
},
|
||||
)
|
||||
|
||||
if err == nil {
|
||||
if len(describeTargets.ScalableTargets) != 0 &&
|
||||
*describeTargets.ScalableTargets[0].ResourceId == rs.Primary.ID {
|
||||
return fmt.Errorf("Application AutoScaling Target still exists")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify error
|
||||
e, ok := err.(awserr.Error)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
if e.Code() != "" {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckAWSAppautoscalingTargetExists(n string, target *applicationautoscaling.ScalableTarget) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No Application AutoScaling Target ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn
|
||||
|
||||
describeTargets, err := conn.DescribeScalableTargets(
|
||||
&applicationautoscaling.DescribeScalableTargetsInput{
|
||||
ResourceIds: []*string{aws.String(rs.Primary.ID)},
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(describeTargets.ScalableTargets) != 1 ||
|
||||
*describeTargets.ScalableTargets[0].ResourceId != rs.Primary.ID {
|
||||
return fmt.Errorf("Application AutoScaling ResourceId not found")
|
||||
}
|
||||
|
||||
*target = *describeTargets.ScalableTargets[0]
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSAppautoscalingTargetAttributes(target *applicationautoscaling.ScalableTarget, resourceId string) resource.TestCheckFunc {
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccAWSAppautoscalingTargetConfig(
|
||||
randClusterName string,
|
||||
randResourceId string,
|
||||
awsAccountId string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_ecs_cluster" "foo" {
|
||||
name = "%s"
|
||||
}
|
||||
resource "aws_ecs_task_definition" "task" {
|
||||
family = "foobar"
|
||||
container_definitions = <<EOF
|
||||
[
|
||||
{
|
||||
"name": "busybox",
|
||||
"image": "busybox:latest",
|
||||
"cpu": 10,
|
||||
"memory": 128,
|
||||
"essential": true
|
||||
}
|
||||
]
|
||||
EOF
|
||||
}
|
||||
resource "aws_ecs_service" "service" {
|
||||
name = "foobar"
|
||||
cluster = "${aws_ecs_cluster.foo.id}"
|
||||
task_definition = "${aws_ecs_task_definition.task.arn}"
|
||||
desired_count = 1
|
||||
|
||||
deployment_maximum_percent = 200
|
||||
deployment_minimum_healthy_percent = 50
|
||||
}
|
||||
resource "aws_appautoscaling_target" "bar" {
|
||||
service_namespace = "ecs"
|
||||
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
|
||||
scalable_dimension = "ecs:service:DesiredCount"
|
||||
role_arn = "arn:aws:iam::%s:role/ecsAutoscaleRole"
|
||||
min_capacity = 1
|
||||
max_capacity = 4
|
||||
}
|
||||
`, randClusterName, awsAccountId)
|
||||
}
|
||||
|
||||
func testAccAWSAppautoscalingTargetConfigUpdate(
|
||||
randClusterName,
|
||||
randResourceId string,
|
||||
awsAccountId string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_ecs_cluster" "foo" {
|
||||
name = "%s"
|
||||
}
|
||||
resource "aws_ecs_task_definition" "task" {
|
||||
family = "foobar"
|
||||
container_definitions = <<EOF
|
||||
[
|
||||
{
|
||||
"name": "busybox",
|
||||
"image": "busybox:latest",
|
||||
"cpu": 10,
|
||||
"memory": 128,
|
||||
"essential": true
|
||||
}
|
||||
]
|
||||
EOF
|
||||
}
|
||||
resource "aws_ecs_service" "service" {
|
||||
name = "foobar"
|
||||
cluster = "${aws_ecs_cluster.foo.id}"
|
||||
task_definition = "${aws_ecs_task_definition.task.arn}"
|
||||
desired_count = 1
|
||||
|
||||
deployment_maximum_percent = 200
|
||||
deployment_minimum_healthy_percent = 50
|
||||
}
|
||||
resource "aws_appautoscaling_target" "bar" {
|
||||
service_namespace = "ecs"
|
||||
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
|
||||
scalable_dimension = "ecs:service:DesiredCount"
|
||||
role_arn = "arn:aws:iam::%s:role/ecsAutoscaleRole"
|
||||
min_capacity = 2
|
||||
max_capacity = 8
|
||||
}
|
||||
`, randClusterName, awsAccountId)
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_appautoscaling_policy"
|
||||
sidebar_current: "docs-aws-resource-appautoscaling-policy"
|
||||
description: |-
|
||||
Provides an Application AutoScaling Policy resource.
|
||||
---
|
||||
|
||||
# aws\_appautoscaling\_policy
|
||||
|
||||
Provides an Application AutoScaling Policy resource.
|
||||
|
||||
## Example Usage
|
||||
```
|
||||
resource "aws_appautoscaling_policy" "down" {
|
||||
name = "scale-down"
|
||||
service_namespace = "ecs"
|
||||
resource_id = "service/ecsclustername/servicename"
|
||||
scalable_dimension = "ecs:service:DesiredCount"
|
||||
|
||||
adjustment_type = "ChangeInCapacity"
|
||||
cooldown = 60
|
||||
metric_aggregation_type = "Maximum"
|
||||
|
||||
step_adjustment {
|
||||
metric_interval_lower_bound = 0
|
||||
scaling_adjustment = -1
|
||||
}
|
||||
depends_on = ["aws_appautoscaling_target.target"]
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `name` - (Required) The name of the policy.
|
||||
* `policy_type` - (Optional) Defaults to "StepScaling" because it is the only option available.
|
||||
* `resource_id` - (Required) The Resource ID on which you want the Application AutoScaling policy to apply to. For Amazon ECS services, this value is the resource type, followed by the cluster name and service name, such as `service/default/sample-webapp`.
|
||||
* `scalable_dimension` - (Optional) The scalable dimension of the scalable target that this scaling policy applies to. The scalable dimension contains the service names- pace, resource type, and scaling property, such as `ecs:service:DesiredCount` for the desired task count of an Amazon ECS service. Defaults to `ecs:service:DesiredCount` since that is the only allowed value.
|
||||
* `service_namespace` - (Optional) The AWS service namespace of the scalable target that this scaling policy applies to. Defaults to `ecs`, because that is currently the only supported option.
|
||||
* `adjustment_type` - (Required) Specifies whether the adjustment is an absolute number or a percentage of the current capacity. Valid values are `ChangeInCapacity`, `ExactCapacity`, and `PercentChangeInCapacity`.
|
||||
* `cooldown` - (Required) The amount of time, in seconds, after a scaling activity completes and before the next scaling activity can start.
|
||||
* `metric_aggregation_type` - (Required) The aggregation type for the policy's metrics. Valid values are "Minimum", "Maximum", and "Average". Without a value, AWS will treat the aggregation type as "Average".
|
||||
* `step_adjustments` - (Optional) A set of adjustments that manage scaling. These have the following structure:
|
||||
```
|
||||
step_adjustment {
|
||||
scaling_adjustment = -1
|
||||
metric_interval_lower_bound = 1.0
|
||||
metric_interval_upper_bound = 2.0
|
||||
}
|
||||
step_adjustment {
|
||||
scaling_adjustment = 1
|
||||
metric_interval_lower_bound = 2.0
|
||||
metric_interval_upper_bound = 3.0
|
||||
}
|
||||
```
|
||||
|
||||
* `scaling_adjustment` - (Required) The number of members by which to
|
||||
scale, when the adjustment bounds are breached. A positive value scales
|
||||
up. A negative value scales down.
|
||||
* `metric_interval_lower_bound` - (Optional) The lower bound for the
|
||||
difference between the alarm threshold and the CloudWatch metric.
|
||||
Without a value, AWS will treat this bound as infinity.
|
||||
* `metric_interval_upper_bound` - (Optional) The upper bound for the
|
||||
difference between the alarm threshold and the CloudWatch metric.
|
||||
Without a value, AWS will treat this bound as infinity. The upper bound
|
||||
must be greater than the lower bound.
|
||||
|
||||
## Attribute Reference
|
||||
* `arn` - The ARN assigned by AWS to the scaling policy.
|
||||
* `name` - The scaling policy's name.
|
||||
* `adjustment_type` - The scaling policy's adjustment type.
|
||||
* `policy_type` - The scaling policy's type.
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_appautoscaling_target"
|
||||
sidebar_current: "docs-aws-resource-appautoscaling-target"
|
||||
description: |-
|
||||
Provides an Application AutoScaling ScalableTarget resource.
|
||||
---
|
||||
|
||||
# aws\_appautoscaling\_target
|
||||
|
||||
Provides an Application AutoScaling ScalableTarget resource.
|
||||
|
||||
## Example Usage
|
||||
```
|
||||
resource "aws_appautoscaling_target" "tgt" {
|
||||
service_namespace = "ecs"
|
||||
resource_id = "service/clusterName/serviceName"
|
||||
scalable_dimension = "ecs:service:DesiredCount"
|
||||
role_arn = "${var.ecs_iam_role}"
|
||||
min_capacity = 1
|
||||
max_capacity = 4
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `name` - (Required) The name of the policy.
|
||||
* `resource_id` - (Required) The Resource ID on which you want the Application AutoScaling policy to apply to. For Amazon ECS services, this value is the resource type, followed by the cluster name and service name, such as `service/default/sample-webapp`.
|
||||
* `scalable_dimension` - (Optional) The scalable dimension of the scalable target. The scalable dimension contains the service namespace, resource type, and scaling property, such as `ecs:service:DesiredCount` for the desired task count of an Amazon ECS service. Defaults to `ecs:service:DesiredCount` since that is the only allowed value.
|
||||
* `service_namespace` - (Optional) The AWS service namespace of the scalable target. Defaults to `ecs`, because that is currently the only supported option.
|
||||
* `max_capacity` - (Required) The max capacity of the scalable target.
|
||||
* `min_capacity` - (Required) The min capacity of the scalable target.
|
||||
* `role_arn` - (Required) The ARN of the IAM role that allows Application AutoScaling to modify your scalable target on your behalf.
|
||||
|
||||
|
||||
## Attribute Reference
|
||||
* `arn` - The ARN assigned by AWS to the scaling policy.
|
||||
* `name` - The scaling policy's name.
|
Loading…
Reference in New Issue