Application Auto Scaling in AWS provides a unified way to scale resources across services such as ECS, SageMaker, DynamoDB, and Aurora. Terraform exposes this capability through the aws_appautoscaling_policy resource, which maps directly to the AWS Application Auto Scaling ScalingPolicy construct. Understanding both the CloudFormation definition and the Terraform implementation is essential to build reliable, drift-resistant scaling configurations.
CloudFormation ScalingPolicy Foundation
The CloudFormation resource AWS::ApplicationAutoScaling::ScalingPolicy defines a scaling policy that Application Auto Scaling uses to adjust the capacity of a scalable target. The resource supports multiple policy types and is the canonical model that Terraform implements.
The syntax declaration for CloudFormation is:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: String
PolicyType: String
PredictiveScalingPolicyConfiguration: PredictiveScalingPolicyConfiguration
ResourceId: String
ScalableDimension: String
ScalingTargetId: String
ServiceNamespace: String
StepScalingPolicyConfiguration: StepScalingPolicyConfiguration
TargetTrackingScalingPolicyConfiguration: TargetTrackingScalingPolicyConfiguration
Key properties from the reference include:
- PolicyName
The name of the scaling policy. Updates to the name of a target tracking scaling policy are not supported, unless you also update the metric used for scaling - PolicyType
- PredictiveScalingPolicyConfiguration
- ResourceId
- ScalableDimension
- ScalingTargetId
- ServiceNamespace
- StepScalingPolicyConfiguration
- TargetTrackingScalingPolicyConfiguration
This property set forms the basis for both Step Scaling and Target Tracking Scaling in Terraform.
Terraform Resource Model and Arguments
The Terraform provider resource aws_appautoscaling_policy provides an Application Auto Scaling Policy resource.
Example usage from the provider documentation:
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:
- 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 Auto Scaling 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
The resource requires a preceding aws_appautoscaling_target to register the scalable resource before attaching policy logic.
Target Tracking Scaling Configuration
Target tracking is the best default for most workloads. Set a CPU or request count target and let AWS handle the math.
In Terraform the configuration is expressed with target_tracking_scaling_policy_configuration. A SageMaker example shows the need for customized metrics and dimensions:
resource "aws_appautoscaling_policy" "sagemaker_policy" {
name = "somepolicy"
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.sagemaker_target.resource_id
scalable_dimension = aws_appautoscaling_target.sagemaker_target.scalable_dimension
service_namespace = aws_appautoscaling_target.sagemaker_target.service_namespace
target_tracking_scaling_policy_configuration {
customized_metric_specification{
metric_name = "ApproximateBacklogSizePerInstance"
namespace = "AWS/SageMaker"
Dimensions = ....?????
statistic = "Average"
}
target_value = 3
scale_in_cooldown =300
scale_out_cooldown = 600
}
}
The Dimensions refer to the CloudWatch dimensions of metric ApproximateBacklogSizePerInstance. For SageMaker variant metrics the dimension is EndpointName with the value set to the endpoint name.
The target definition for that example is:
resource "aws_appautoscaling_target" "sagemaker_target" {
max_capacity = 3
min_capacity = 1
resource_id = "myendpoint"
scalable_dimension = "sagemaker:variant:DesiredInstanceCount"
service_namespace = "sagemaker"
}
Target tracking configuration properties include TargetValue, CustomizedMetricSpecification with MetricName, Namespace, Dimensions, Statistic, and cooldown settings for scale in and scale out.
Step Scaling Configuration
It is useful when you need non-linear scaling behavior. Step scaling allows different adjustments based on metric intervals.
A scale out example:
resource "aws_appautoscaling_policy" "scale_out" {
name = "myapp-scale-out"
policy_type = "StepScaling"
resource_id = aws_appautoscaling_target.ecs.resource_id
scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
service_namespace = aws_appautoscaling_target.ecs.service_namespace
step_scaling_policy_configuration {
adjustment_type = "ChangeInCapacity"
cooldown = 60
metric_aggregation_type = "Average"
step_adjustment {
metric_interval_lower_bound = 0
metric_interval_upper_bound = 15
scaling_adjustment = 2
}
step_adjustment {
metric_interval_lower_bound = 15
metric_interval_upper_bound = 25
scaling_adjustment = 4
}
step_adjustment {
metric_interval_lower_bound = 25
scaling_adjustment = 6
}
}
}
A scale in counterpart uses a longer cooldown to prevent premature reductions:
resource "aws_appautoscaling_policy" "scale_in" {
name = "myapp-scale-in"
policy_type = "StepScaling"
resource_id = aws_appautoscaling_target.ecs.resource_id
scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
service_namespace = aws_appautoscaling_target.ecs.service_namespace
step_scaling_policy_configuration {
adjustment_type = "ChangeInCapacity"
cooldown = 300
metric_aggregation_type = "Average"
step_adjustment {
metric_interval_upper_bound = 0
scaling_adjustment = -1
}
}
}
Step scaling works in conjunction with CloudWatch alarms that trigger the policy via alarm_actions.
Module Parameters for Reusable Policies
A reusable Terraform module for app autoscaling exposes parameters to control policy creation.
Module variables include:
- app
the name of the application expressed as an acronym
string
n/a
yes - env
the target tier ('dev', 'qa', 'stage', or 'prod'.)
string
n/a
yes - policy_type
scaling policy - either 'StepScaling' or 'TargetTrackingScaling'
string
"TargetTrackingScaling"
no - predefinedmetrictype
the name of the pre-defined aws cloudwatch metric type to base autoscaling activities on
string
"ECSServiceAverageCPUUtilization"
no - program
the program associated with the application
string
n/a
yes - resource_id
resource type and unique identifier string for the resource associated with the scaling policy - use outputs from appautoscaling target resource
string
n/a
yes - resourcenamesuffix
resource name suffix that follows the stack name
string
n/a
yes - scalable_dimension
calable dimension of the scalable target - use outputs from appautoscaling target resource
string
n/a
yes - service_namespace
aws service namespace of the scalable target - use outputs from the appautoscaling target resource
string
n/a
yes - target_value
target value of the metric to invoke autoscaling activity
number
80
no
The module defaults to TargetTrackingScaling with a predefined metric of ECSServiceAverageCPUUtilization and a target value of 80.
ECS Auto Scaling Patterns and Drift Prevention
ECS auto scaling in Terraform starts with awsappautoscalingtarget to register the service, then awsappautoscalingpolicy for the scaling logic.
To prevent Terraform from overriding auto scaler adjustments, a lifecycle block is used:
lifecycle {
ignore_changes = [desired_count]
}
Without this, every terraform apply would reset the task count to whatever is in your Terraform config, overriding the auto scaler's adjustments.
Choosing Scaling Thresholds
The right thresholds depend on your application, but here are guidelines:
- CPU target tracking at 70% leaves headroom for traffic spikes while still using resources efficiently
- Scale-out cooldown of 60 seconds lets you respond quickly to growing demand
- Scale-in cooldown of 300 seconds prevents premature scale-in during temporary dips
- Always keep minimum tasks >= 2 for availability across AZs
- Use ALBRequestCountPerTarget for web services - it responds to load before CPU saturates
Outputs from an ECS autoscaling setup typically include:
output "scaling_target_resource_id" {
value = aws_appautoscaling_target.ecs.resource_id
}
output "min_capacity" {
value = aws_appautoscaling_target.ecs.min_capacity
}
output "max_capacity" {
value = aws_appautoscaling_target.ecs.max_capacity
}
A complete ECS pattern combines target tracking, step scaling, and scheduled scaling for the most responsive setup.
CloudWatch Alarm Integration
Step scaling policies are often triggered by CloudWatch metric alarms.
Scale out alarm example:
resource "aws_cloudwatch_metric_alarm" "cpu_high" {
alarm_name = "myapp-cpu-high"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/ECS"
period = 60
statistic = "Average"
threshold = 70
dimensions = {
ClusterName = var.ecs_cluster_name
ServiceName = aws_ecs_service.app.name
}
alarm_actions = [aws_appautoscaling_policy.scale_out.arn]
}
Scale in alarm example:
resource "aws_cloudwatch_metric_alarm" "cpu_low" {
alarm_name = "myapp-cpu-low"
comparison_operator = "LessThanThreshold"
evaluation_periods = 5
metric_name = "CPUUtilization"
namespace = "AWS/ECS"
period = 60
statistic = "Average"
threshold = 30
dimensions = {
ClusterName = var.ecs_cluster_name
ServiceName = aws_ecs_service.app.name
}
alarm_actions = [aws_appautoscaling_policy.scale_in.arn]
}
Scheduled Scaling for Predictable Traffic
If your traffic patterns are predictable like business hours, schedule scaling changes:
resource "aws_appautoscaling_scheduled_action" "scale_up" {
name = "myapp-scale-up-business-hours"
service_namespace = aws_appautoscaling_target.ecs.service_namespace
resource_id =
Scheduled actions complement reactive policies by pre-warming capacity.
Policy Type Comparison
| Feature | StepScaling | TargetTrackingScaling |
| PolicyType value | StepScaling | TargetTrackingScaling |
| Control | Manual steps with metric intervals | Automatic target value maintenance |
| Cooldown | Per policy | ScaleInCooldown, ScaleOutCooldown |
| Metric | CloudWatch alarm driven | Predefined or customized metric |
| Use case | Non-linear scaling, fine grained control | Default stable load handling |
Conclusion
Terraform awsappautoscalingpolicy provides a direct mapping to AWS Application Auto Scaling scaling policies with support for StepScaling, TargetTrackingScaling, and PredictiveScaling via CloudFormation properties. The resource requires a matching awsappautoscalingtarget to define resourceid, scalabledimension, and servicenamespace. Target tracking is the best default for most workloads, with a CPU target around 70% and a scale-out cooldown of 60 seconds and scale-in cooldown of 300 seconds. Step scaling enables non-linear adjustments with multiple stepadjustment blocks keyed to metric intervals. Customized metrics such as SageMaker ApproximateBacklogSizePerInstance require explicit Dimensions mapping to CloudWatch dimensions. Drift prevention via lifecycle ignorechanges on desiredcount is essential for ECS services to allow the auto scaler to manage capacity without Terraform resets. Combining target tracking, step scaling, scheduled actions, and proper cooldowns yields a responsive and stable auto scaling system across ECS, SageMaker, and other AWS services.