The aws:appautoscaling/target:Target resource, part of the Pulumi AWS provider, registers AWS resources as scalable targets, defining their capacity bounds and enabling Application AutoScaling to adjust their size. This guide focuses on four capabilities: DynamoDB table and index capacity, ECS service task counts, Aurora read replicas, and MSK broker storage.
Scalable targets reference existing AWS resources and typically rely on AWS-managed IAM Service-Linked Roles. The examples are intentionally small. Combine them with your own scaling policies (via aws.appautoscaling.Policy) and monitoring.
Scale DynamoDB table read capacity
Applications using DynamoDB often need to scale read capacity automatically as traffic patterns change throughout the day.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const dynamodbTableReadTarget = new aws.appautoscaling.Target("dynamodb_table_read_target", {
maxCapacity: 100,
minCapacity: 5,
resourceId: `table/${example.name}`,
scalableDimension: "dynamodb:table:ReadCapacityUnits",
serviceNamespace: "dynamodb",
});
import pulumi
import pulumi_aws as aws
dynamodb_table_read_target = aws.appautoscaling.Target("dynamodb_table_read_target",
max_capacity=100,
min_capacity=5,
resource_id=f"table/{example['name']}",
scalable_dimension="dynamodb:table:ReadCapacityUnits",
service_namespace="dynamodb")
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/appautoscaling"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := appautoscaling.NewTarget(ctx, "dynamodb_table_read_target", &appautoscaling.TargetArgs{
MaxCapacity: pulumi.Int(100),
MinCapacity: pulumi.Int(5),
ResourceId: pulumi.Sprintf("table/%v", example.Name),
ScalableDimension: pulumi.String("dynamodb:table:ReadCapacityUnits"),
ServiceNamespace: pulumi.String("dynamodb"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var dynamodbTableReadTarget = new Aws.AppAutoScaling.Target("dynamodb_table_read_target", new()
{
MaxCapacity = 100,
MinCapacity = 5,
ResourceId = $"table/{example.Name}",
ScalableDimension = "dynamodb:table:ReadCapacityUnits",
ServiceNamespace = "dynamodb",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.appautoscaling.Target;
import com.pulumi.aws.appautoscaling.TargetArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var dynamodbTableReadTarget = new Target("dynamodbTableReadTarget", TargetArgs.builder()
.maxCapacity(100)
.minCapacity(5)
.resourceId(String.format("table/%s", example.name()))
.scalableDimension("dynamodb:table:ReadCapacityUnits")
.serviceNamespace("dynamodb")
.build());
}
}
resources:
dynamodbTableReadTarget:
type: aws:appautoscaling:Target
name: dynamodb_table_read_target
properties:
maxCapacity: 100
minCapacity: 5
resourceId: table/${example.name}
scalableDimension: dynamodb:table:ReadCapacityUnits
serviceNamespace: dynamodb
The resourceId property identifies the table using the format table/{table-name}. The scalableDimension specifies which metric to scale (here, read capacity units). The serviceNamespace tells Application AutoScaling which AWS service manages this resource. Together, minCapacity and maxCapacity define the scaling bounds: Application AutoScaling adjusts provisioned throughput between 5 and 100 units based on demand.
Scale DynamoDB global secondary index capacity
Global secondary indexes often have different access patterns than their base tables, requiring independent scaling.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const dynamodbIndexReadTarget = new aws.appautoscaling.Target("dynamodb_index_read_target", {
maxCapacity: 100,
minCapacity: 5,
resourceId: `table/${example.name}/index/${indexName}`,
scalableDimension: "dynamodb:index:ReadCapacityUnits",
serviceNamespace: "dynamodb",
});
import pulumi
import pulumi_aws as aws
dynamodb_index_read_target = aws.appautoscaling.Target("dynamodb_index_read_target",
max_capacity=100,
min_capacity=5,
resource_id=f"table/{example['name']}/index/{index_name}",
scalable_dimension="dynamodb:index:ReadCapacityUnits",
service_namespace="dynamodb")
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/appautoscaling"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := appautoscaling.NewTarget(ctx, "dynamodb_index_read_target", &appautoscaling.TargetArgs{
MaxCapacity: pulumi.Int(100),
MinCapacity: pulumi.Int(5),
ResourceId: pulumi.Sprintf("table/%v/index/%v", example.Name, indexName),
ScalableDimension: pulumi.String("dynamodb:index:ReadCapacityUnits"),
ServiceNamespace: pulumi.String("dynamodb"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var dynamodbIndexReadTarget = new Aws.AppAutoScaling.Target("dynamodb_index_read_target", new()
{
MaxCapacity = 100,
MinCapacity = 5,
ResourceId = $"table/{example.Name}/index/{indexName}",
ScalableDimension = "dynamodb:index:ReadCapacityUnits",
ServiceNamespace = "dynamodb",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.appautoscaling.Target;
import com.pulumi.aws.appautoscaling.TargetArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var dynamodbIndexReadTarget = new Target("dynamodbIndexReadTarget", TargetArgs.builder()
.maxCapacity(100)
.minCapacity(5)
.resourceId(String.format("table/%s/index/%s", example.name(),indexName))
.scalableDimension("dynamodb:index:ReadCapacityUnits")
.serviceNamespace("dynamodb")
.build());
}
}
resources:
dynamodbIndexReadTarget:
type: aws:appautoscaling:Target
name: dynamodb_index_read_target
properties:
maxCapacity: 100
minCapacity: 5
resourceId: table/${example.name}/index/${indexName}
scalableDimension: dynamodb:index:ReadCapacityUnits
serviceNamespace: dynamodb
The resourceId format extends to table/{table-name}/index/{index-name} for GSI scaling. The scalableDimension changes to dynamodb:index:ReadCapacityUnits to target the index specifically. This allows you to optimize costs and performance for specific query workloads without affecting the base table’s capacity.
Scale ECS service task count
Container workloads running in ECS need to scale task count based on metrics like CPU utilization or request count.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const ecsTarget = new aws.appautoscaling.Target("ecs_target", {
maxCapacity: 4,
minCapacity: 1,
resourceId: `service/${example.name}/${exampleAwsEcsService.name}`,
scalableDimension: "ecs:service:DesiredCount",
serviceNamespace: "ecs",
});
import pulumi
import pulumi_aws as aws
ecs_target = aws.appautoscaling.Target("ecs_target",
max_capacity=4,
min_capacity=1,
resource_id=f"service/{example['name']}/{example_aws_ecs_service['name']}",
scalable_dimension="ecs:service:DesiredCount",
service_namespace="ecs")
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/appautoscaling"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := appautoscaling.NewTarget(ctx, "ecs_target", &appautoscaling.TargetArgs{
MaxCapacity: pulumi.Int(4),
MinCapacity: pulumi.Int(1),
ResourceId: pulumi.Sprintf("service/%v/%v", example.Name, exampleAwsEcsService.Name),
ScalableDimension: pulumi.String("ecs:service:DesiredCount"),
ServiceNamespace: pulumi.String("ecs"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var ecsTarget = new Aws.AppAutoScaling.Target("ecs_target", new()
{
MaxCapacity = 4,
MinCapacity = 1,
ResourceId = $"service/{example.Name}/{exampleAwsEcsService.Name}",
ScalableDimension = "ecs:service:DesiredCount",
ServiceNamespace = "ecs",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.appautoscaling.Target;
import com.pulumi.aws.appautoscaling.TargetArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var ecsTarget = new Target("ecsTarget", TargetArgs.builder()
.maxCapacity(4)
.minCapacity(1)
.resourceId(String.format("service/%s/%s", example.name(),exampleAwsEcsService.name()))
.scalableDimension("ecs:service:DesiredCount")
.serviceNamespace("ecs")
.build());
}
}
resources:
ecsTarget:
type: aws:appautoscaling:Target
name: ecs_target
properties:
maxCapacity: 4
minCapacity: 1
resourceId: service/${example.name}/${exampleAwsEcsService.name}
scalableDimension: ecs:service:DesiredCount
serviceNamespace: ecs
The resourceId follows the pattern service/{cluster-name}/{service-name}. The scalableDimension ecs:service:DesiredCount tells Application AutoScaling to adjust the number of running tasks. When paired with a scaling policy, this configuration automatically adds or removes tasks between 1 and 4 based on demand.
Scale Aurora read replica count
Read-heavy database workloads benefit from adding Aurora read replicas during peak hours and removing them during quiet periods.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const replicas = new aws.appautoscaling.Target("replicas", {
serviceNamespace: "rds",
scalableDimension: "rds:cluster:ReadReplicaCount",
resourceId: `cluster:${example.id}`,
minCapacity: 1,
maxCapacity: 15,
});
import pulumi
import pulumi_aws as aws
replicas = aws.appautoscaling.Target("replicas",
service_namespace="rds",
scalable_dimension="rds:cluster:ReadReplicaCount",
resource_id=f"cluster:{example['id']}",
min_capacity=1,
max_capacity=15)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/appautoscaling"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := appautoscaling.NewTarget(ctx, "replicas", &appautoscaling.TargetArgs{
ServiceNamespace: pulumi.String("rds"),
ScalableDimension: pulumi.String("rds:cluster:ReadReplicaCount"),
ResourceId: pulumi.Sprintf("cluster:%v", example.Id),
MinCapacity: pulumi.Int(1),
MaxCapacity: pulumi.Int(15),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var replicas = new Aws.AppAutoScaling.Target("replicas", new()
{
ServiceNamespace = "rds",
ScalableDimension = "rds:cluster:ReadReplicaCount",
ResourceId = $"cluster:{example.Id}",
MinCapacity = 1,
MaxCapacity = 15,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.appautoscaling.Target;
import com.pulumi.aws.appautoscaling.TargetArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var replicas = new Target("replicas", TargetArgs.builder()
.serviceNamespace("rds")
.scalableDimension("rds:cluster:ReadReplicaCount")
.resourceId(String.format("cluster:%s", example.id()))
.minCapacity(1)
.maxCapacity(15)
.build());
}
}
resources:
replicas:
type: aws:appautoscaling:Target
properties:
serviceNamespace: rds
scalableDimension: rds:cluster:ReadReplicaCount
resourceId: cluster:${example.id}
minCapacity: 1
maxCapacity: 15
The resourceId uses cluster:{cluster-id} to identify the Aurora cluster. The scalableDimension rds:cluster:ReadReplicaCount targets the number of read replicas. Application AutoScaling can add up to 15 replicas during high load and scale down to 1 during quiet periods, optimizing both performance and costs.
Scale MSK broker storage volume size
Kafka clusters running on MSK can automatically expand broker storage as data volume grows, preventing out-of-space errors.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const mskTarget = new aws.appautoscaling.Target("msk_target", {
serviceNamespace: "kafka",
scalableDimension: "kafka:broker-storage:VolumeSize",
resourceId: example.arn,
minCapacity: 1,
maxCapacity: 8,
});
import pulumi
import pulumi_aws as aws
msk_target = aws.appautoscaling.Target("msk_target",
service_namespace="kafka",
scalable_dimension="kafka:broker-storage:VolumeSize",
resource_id=example["arn"],
min_capacity=1,
max_capacity=8)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/appautoscaling"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := appautoscaling.NewTarget(ctx, "msk_target", &appautoscaling.TargetArgs{
ServiceNamespace: pulumi.String("kafka"),
ScalableDimension: pulumi.String("kafka:broker-storage:VolumeSize"),
ResourceId: pulumi.Any(example.Arn),
MinCapacity: pulumi.Int(1),
MaxCapacity: pulumi.Int(8),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var mskTarget = new Aws.AppAutoScaling.Target("msk_target", new()
{
ServiceNamespace = "kafka",
ScalableDimension = "kafka:broker-storage:VolumeSize",
ResourceId = example.Arn,
MinCapacity = 1,
MaxCapacity = 8,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.appautoscaling.Target;
import com.pulumi.aws.appautoscaling.TargetArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var mskTarget = new Target("mskTarget", TargetArgs.builder()
.serviceNamespace("kafka")
.scalableDimension("kafka:broker-storage:VolumeSize")
.resourceId(example.arn())
.minCapacity(1)
.maxCapacity(8)
.build());
}
}
resources:
mskTarget:
type: aws:appautoscaling:Target
name: msk_target
properties:
serviceNamespace: kafka
scalableDimension: kafka:broker-storage:VolumeSize
resourceId: ${example.arn}
minCapacity: 1
maxCapacity: 8
The resourceId references the MSK cluster ARN directly. The scalableDimension kafka:broker-storage:VolumeSize targets broker storage capacity. Application AutoScaling expands storage from 1 to 8 units as needed, ensuring your Kafka cluster never runs out of space.
Beyond these examples
These snippets focus on specific scalable target features: DynamoDB table and index capacity scaling, ECS task count adjustment, and Aurora replica and MSK storage scaling. They’re intentionally minimal rather than full autoscaling solutions.
The examples reference pre-existing infrastructure such as DynamoDB tables and indexes, ECS clusters and services, and Aurora clusters and MSK clusters. They focus on registering scalable targets rather than provisioning the underlying resources.
To keep things focused, common scalable target patterns are omitted, including:
- IAM role configuration (roleArn, usually auto-managed)
- Suspended state controls (suspendedState)
- Tagging and metadata (tags)
- Scaling policies (configured via aws.appautoscaling.Policy)
These omissions are intentional: the goal is to illustrate how each scalable target is wired, not provide drop-in autoscaling modules. See the Application AutoScaling Target resource reference for all available configuration options.
Let's configure AWS Application Auto Scaling Targets
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Immutability
resourceId, scalableDimension, and serviceNamespace properties are immutable and require resource replacement if changed.The format varies by service:
- DynamoDB table:
table/{tableName} - DynamoDB index:
table/{tableName}/index/{indexName} - ECS service:
service/{clusterName}/{serviceName} - RDS cluster:
cluster:{clusterId} - MSK cluster: Use the cluster ARN directly
IAM & Permissions
roleArn are ignored by the API for those namespaces.aws.iam.ServiceLinkedRole if needed.Tags & Lifecycle
arn and cannot use tags or participate in defaultTags. Use lifecycle.ignore_changes to suppress these differences that can never be reconciled.lifecycle.ignore_changes to your resource configuration to prevent Pulumi from showing tag-related diffs for targets created before 2023-03-20.