App::FargateStack
# 1. Create a configuration stub for a one-shot task
export AWS_PROFILE=my-profile
app-FargateStack create-stack my-stack task:helloworld image:helloworld
# 2. Dry-run and analyze the configuration
app-FargateStack plan
# 3. Provision the full stack
app-FargateStack apply
# 4. Run the task
app-FargateStack run-task
# 5. View the provisioned configuration file
cat my-stack.yml
See "GETTING STARTED" for more details.
App::FargateStack is a lightweight deployment framework for Amazon ECS on Fargate. It enables you to define and launch containerized services with minimal AWS-specific knowledge and virtually no boilerplate. Designed to simplify cloud infrastructure without sacrificing flexibility, the framework lets you declaratively specify tasks, IAM roles, log groups, secrets, and networking in a concise YAML configuration.
By automating the orchestration of ALBs, security groups, EFS mounts, CloudWatch logs, and scheduled or daemon tasks, App::FargateStack reduces the friction of getting secure, production-grade workloads running in AWS. You supply a config file, and the tool intelligently discovers or provisions required resources.
It supports common service types such as HTTP, HTTPS, daemon, and cron tasks, and handles resource scoping, role-based access, and health checks behind the scenes. It assumes a reasonable AWS account layout and defaults, but gives you escape hatches where needed.
App::FargateStack is ideal for developers who want the power of ECS and Fargate without diving into the deep end of Terraform, CloudFormation, or the AWS Console.
This is a work in progress. Versions prior to 1.1.0 are considered usable but may still contain issues related to edge cases or uncommon configuration combinations.
This documentation corresponds to version 1.0.55.
The release of version 1.1.0 will mark the first production-ready release. Until then, you're encouraged to try it out and provide feedback. Issues or feature requests can be submitted via GitHub.
This class is implemented as a modulino and is not designed for traditional object-oriented use. As such, this section is intentionally omitted.
Command Arguments Description
------- --------- -----------
add-scaling-policy See Note 12 adds an autoscaling policy to the configuration
add-schedule-action See Note 13 adds a scheduled scaling action
apply reads config and creates resources
create-stack app-name service-clauses... creates a new stack configuration
delete-scaling-policy task-name deletes the autoscaling policy for a task from your configuration
delete-scheduled-action action-name deletes a named scheduled action from your configuration
delete-service task-name alias for remove-service
delete-task task-name deletes all resources associated with a task (See Note 11)
delete-autoscaling-policy task-name deletes a metric based scaling policy for the task
delete-scheduled-action action-name deletes an existing autoscaling scheduled action
delete-scheduled-task task-name deletes all resources associated with a scheduled task (See Note 11)
delete-daemon task-name deletes all resources associated with a daemon (See Note 11)
delete-http-service task-name deletes all resources associated with a http service (See Note 11)
deploy-service task-name create a new service (see Note 4)
destroy removes all resources in your stack that were provisioned by App::FargateStack
disable-scheduled-task task-name disable a scheduled task
enable-scheduled-task t ask-name enable a scheduled task
help [subject] displays general help or help on a particular subject (see Note 2)
list-tasks list running or stopped tasks
list-zones domain list the hosted zones for a domain
logs task-name start end display CloudWatch logs (see Note 5)
plan reads config and reports on resource creation
register-task-definition task-name creates a new task definition revision
remove-service task-name removes an existing service but does not delete the task
run-task task-name launches an adhoc task
show command args output additional info about the stack or run states
cloudtrail-events task-name start-time [end-time] show cloudtrail events for a scheduled task (useful for debugging)
stack shows a summary of the stack configuration
start-service task-name [count] starts a service
status task-name provides the current status for a task
stop-service task-name stops a running service
tasks displays a summary of all tasks in your stack
update-policy updates the ECS policy in the event of resource changes
update-target task-name force update of target definition
version display the current version number
-h, --help help
--cache, --no-cache use the configuration file as the source of truth (see Note 8)
-c, --config path to the .yml configuration
-C, --create-alb forces creation of a new ALB, prevents use of an existing ALB
--color, --no-color default: color
--confirm-all confirm deletion of all resources
-d, --dryrun just report actions, do not apply
--dns-profile alias for --route53-profile
-f, --force force action (depends on context)
--history, --no-history save cli parameters to .fargatestack/defaults.json
--log-level 'trace', 'debug', 'info', 'warn', 'error', default: info (See Note 6)
--log-time, --no-log-time for logs command, output CloudWatch timestamp (default: --no-log-time)
--log-wait, --no-log-wait for logs command, continue to monitor logs (default: --log-wait)
--log-poll-time amount of time in seconds to sleep between requesting new log events
--max-events, -m maximum number of events to show for status command (default: 5)
--output output type for some commands, valid values: text|json
--no-profile do not use --profile in AWS CLI commands (See Note 14)
-p, --profile AWS profile (see Note 1)
--purge-config remove deleted tasks from multi-task configs
--route53-profile set this if your Route 53 zones are in a different account (See Note 10)
-s, --skip-register skips registering a new task definition when using update-target (See Note 7)
-u, --update, --no-update update config (See Note 9)
-U, --unlink, --no-unlink delete or keep temp files (default: --unlink)
-w, --wait, --no-wait wait for tasks to complete and then dump the log (applies to adhoc tasks)
-v, --version script version
(1) Use the --profile option to override the profile defined in
the configuration file.
Note: The Route 53 service uses the same profile unless you specify
--route53-profile or set a profile name in the route53 section
of the configuration file.
(2) You can get help using the --help option or use the help
command with a subject or one of the commands.
app-FargateStack help overview
app-FargateStack help redeploy
If you do not provide a subject then you will get the same information
as --help. Use help help to get a list of available subjects.
(3) You must log at least at the 'info' level to report
progress. This is set for you when your plan or apply.
apply, run app-FargateStack
deploy-service task-name. This will launch your service with a count
of 1 task. You can optionally specify a different count after the task
name.(5) You can tail or display a set of log events from a task's log stream:
app-Fargate logs [--log-wait] [--log-time] start end
--log-wait --no-log-wait (optional)
Continue to monitor stream and dump logs to STDOUT
default: --log-wait
--log-time, --no-log-time (optional)
Output the CloudWatch timestamp of the message.
default: --log-time
task-name
The name of the task whose logs you want to view.
start
Starting date and optionally time of the log events to display. Format can be one of:
Nd => N days ago
Nm => N minutes ago
Nh => N hours ago
mm/dd/yyyy
mm/dd/yyyy hh:mm::ss
end
If provided both start and end must date-time strings.
(6) The default log level is 'info' which will create an audit trail of resource provisioning. Certain commands log at the 'error' level to reduce console noise. Logging at lower levels will prevent potential useful messages from being displayed. To see the AWS CLI commands being executed, log at the 'debug' level. The 'trace' level will output the result of the AWS CLI commands.
--skip-register if you want to update a tasks target
rule without registering a new task definition. This is typically done
if for some reason your target rule is out of sync with your task
definition version.--no-cache and run plan. In
most cases this should not be necessary as the framework will
invalidate the configuration if an error occurs forcing a re-sync on
the next run of plan or apply.--no-update is not permitted with apply. If you need a
dry plan without applying or updating the config, use --dryrun (and
optionally --no-update) with plan.--route53-profile to the profile that has
permissions to manage your hosted zones. By default the script will
use the default profile.delete-service) instead of
deleting all of the resources.These resources will NOT be removed:
- ECR image associated with a task
- An ACM certificate provisioned by App::FargateStack
(12) This command will add a scaling policy to an HTTP, HTTPS or
daemon task. In order to apply the policy you must run plan &
apply. You provide the following arguments in order:
[task-name] metric-type metric-value [min-capacity max-capacity [scale-out-cooldown scale-in-cooldown]]
task-name is optional if you only have 1 scalable task.min-capacity, max-capacity are optional and will default to 1 and 2 respectively.scale-out-cooldown, scale-in-cooldown are optional. If
you provided you must include the capacity paramters.
app-FargateStack apache requests 500 2 3 60 300
(13) This command will add a schedule scaling action to your
configuration. In order to activate the schedule you must run plan
and apply. You provide the following arguments in order:
[task-name] action-name start-time end-time days scale-out-capacity scale-in-capacity
task-name is optional if you only have 1 scalable task.action-name is a name for your schedule. It must be
unique within your entire configuration.start-time is UTC. The format for the starting time is
MM:HH. (Example: 00:18)days is the day or days of the week for the scheduled action.
Note: Days should be one of MON,TUE,WED,THU,FRI,SAT or 1-7
Example:
Scale out to 4 tasks at 10pm (EDT) for 30 minutes to run a batch job on Friday night.
00:02 30:02 SAT 4/1 4/1
Note that the cron specification is in UTC, hence we run at 2am for 30 minutes on Saturday morning in UTC.
end-time time to scale back in. Same format as start-time
scale-out-capacity, scale-in-capacity - These options
represent the scale out and scale in capacities.
Each value should be a tuple separated by '/', ',', ':' or '-'. The
first value represents the minimum or maximum capacity for scaling out
or in at the specified starting time of schedule action. The second
value represents the minimum or maximum capacity for scaling in or out
at the ending time of the action.
Example to scale out to 2 tasks during business hours of 8:30am and
5:30pm and scale in to 1 task during non-business hours.
app-FargateStack add-scheduled-action business_hours 30:12 30:21 MON-FRI 2/1 2/1
If you had a scaling policy, your scaling policies `max_capacity`
must be greater than or equal to the largest maximum capacity of your
all of you scheduled actions for that task.
app-FargateStack add-scheduled-action business_hours 30:12 30:21 2/1 4/1
In this case, your scaling policy `max_capacity` value must be at least
4.
(14) Use the --no-profile option if you are using an IAM
instance role.
NOTE: This is a brief introduction to App::FargateStack. To see a
list of topics providing more detail use the help help command.
The App::FargateStack framework, as its name implies provides
developers with a tool to create Fargate tasks and services. It has
been designed to make creating and launching Fargate based services as
simple as possible. Accordingly, it provides logical and pragmatic
defaults based on the common uses for Fargate based applications. You
can however customize many of the resources being built by the script.
Using a YAML based configuration file, you specify your required
resources and their attributes, run the app-FargateStack script and
launch your application.
Using this framework you can:
app-FargateStack multiple times (idempotency)Getting a Fargate task up and running requires that you provision and configure multiple AWS resources. Stitching it together using Terraform or CloudFormation can be tedious and time consuming, even if you know what resources to provision AND how to stitch it together.
The motivation behind writing this framework was to take the drudgery of writing declarative resource generators for all of the resources required to run a simple task, create basic web applications or RESTful APIs. Instead, we wanted a framework that covered 90% of our use cases while allowing our development workflow to go something like:
Of course, this is only a "good idea" if creating the initial configuration file is truly minimal, otherwise it becomes an exercise similar to using Terraform or CloudFormation. So what is the minimum amount of configuration to inform our framework so it can create our Fargate worker? How's this for minimal?
---
app:
name: my-stack
tasks:
my-worker:
type: task
image: my-worker:latest
schedule: cron(50 12 * * * *)
TIP: You can use the "create-stack" command to create minimal configuration files for various Fargate application scenarios.
Using this minimal configuration and running app-FargateStack like this:
app-FargateStack plan
...the framework would create the following resources in your VPC:
my-stack-cluster...so as you can see, rolling all of this by hand could be a daunting task and one made even more difficult when you decide to use other AWS resources inside your task like buckets, queues or an EFS file system!
Creating a web application using a minimal configuration works too. To build a web application you can start with this minimal configuration:
---
app:
name: my-web-app
domain: my-web-app.example.com
tasks:
apache:
type: https
image: my-web-app:latest
This will create an externally facing web application for you with these resources:
Once again, launching a Fargate service requires a lot of fiddling with AWS resources! Getting all of the plumbing installed and working requires a lot of what and how knowledge.
Adding or updating resources for an existing application should also
be easy. Updating the infrastructure should just be a matter of
updating the configuration and re-running the framework's script. When
you update the configuration the App::FargateStack will detect the
changes and update the necessary resources.
Currently the framework supports adding a single SQS queue, a single S3 bucket, volumes using EFS mount points, environment variables and secrets from AWS Secrets Manager.
bucket:
name: my-worker-bucket
queue:
name: my-worker-queue
tasks:
my-worker:
image: my-worker:latest
command: /usr/local/bin/my-worker.pl
type: task
schedule: cron(00 15 * * * *)
environment:
ENVIRONMENT=prod
secrets:
db_password:DB_PASSWORD
efs:
id: fs-abcde12355
path: /
mount_point: /mnt/my-worker
Adding new resources would normally require you to update your policies to allow your worker to access these resource. However, the framework automatically detects that the policy needs to be updated when new resources are added (even secrets) and takes care of that for you.
See app-Fargate help configuration for more information about
resources and options.
The framework attempts to be as transparent as possible regarding what it is doing, how long it takes, what the result was and most importantly what defaults were used during resource provisioning. Every time the framework is run, the configuration file is updated based on any new resources provisioned or configured. For example, if you did not specify subnets, they are inferred by inspecting your VPC and automatically added to the configuration file.
This gives you a single view into your Fargate application
When enabled, App::FargateStack automatically remembers the most recently
used values for several CLI options between runs. This feature helps streamline
repetitive workflows by eliminating the need to re-specify common arguments
such as the AWS profile, region, or config file.
The following options are tracked and persisted:
--profile--region--config--route53-profile--max-eventsThese values are stored in .fargatestack/defaults.json within your current
project directory. If you omit any of these options on subsequent runs, the
most recently used value will be reused.
Typically, you would create a dedicated project directory for your
stack and place your configuration file there. Once you invoke a
command that includes any of the tracked CLI options, the
.fargatestack/defaults.json file will be created
automatically. Future commands run from that directory can then omit
those options. A typical workflow to create a new stack with a
scheduled task might look like this:
mkdir my-project
cd my-project
app-FargateStack create-stack foo task:my-cron image:my-project 'schedule:cron(0 10 * * * *)'
app-FargateStack plan
app-FargateStack apply
That's it...you just created a scheduled task that will run at 10 AM every day!
Use the --no-history option to temporarily disable this feature for a single
run. This allows you to override stored values without modifying or deleting
them.
To clear all saved defaults entirely, use the reset-history command. This
removes all of the tracked values from the .fargatestack/defaults.json file,
but preserves the file itself.
Only explicitly provided CLI options are tracked. Values derived from environment variables or configuration files are not saved.
This feature is enabled by default.
The basic syntax of the framework's CLI is:
app-FargateStack command --config fargate-stack.yml [options] command-args
You must provide at least a command.
Your configuration file can be named anything, but by convention your
configuration file should have a .yml extension. If you don't
provide a configuration filename the default configuration file
fargate-stack.yml will be used. You can also set the
FARGATE_STACK_CONFIG environment variable to the name of your
configuration file.
info). You can see what AWS commands are being executed using the
debug level. If you'd like see the results of the AWS CLI commands use the
trace level.status, logs, list-tasks, list-zone, etc) will log
at the error level which will eliminate log noise on the console.--no-color if you don't like color.help [subject]
Displays basic usage or help on a particular subject. To see a list of
help subject use help help. The script will attemp to do a regexp
match if you do provide the exact help topic, so you can cheat and use
shortened versions of the topic.
help cloudwatch
Alias for add-scaling-policy.
This command will add a scaling policy to an HTTP, HTTPS or
daemon task. In order to apply the policy you must run plan &
apply. You provide the following arguments in order:
[task-name] metric-type metric-value [min-capacity max-capacity [scale-out-cooldown scale-in-cooldown]]
Example:
app-FargateStack add-scaling-policy cpu 60 1 3
task-name
The task in your configuration that will contain the new scaling policy. This is optional if you only have 1 scalable task.
metric-type (required)
One of cpu or requests
metric-value (required)
The metric value. For cpu it should be an integer between 1 and
100. For requests it should be a count representing the number of
requests your ALB receives per minute.
min-capacity
The minimum number of tasks to maintain.
default: 1
max-capacity
The maximum number of tasks to scale up.
default: 2
scale-out-cooldown
The number of seconds to wait before scaling up another task.
default: 60
scale-in-cooldown
The number of seconds to wait until scaling down a task.
default: 300 (5 minutes)
This command will add a schedule scaling action to your
configuration. In order to activate the schedule you must run plan
and apply. You provide the following arguments in order:
[task-name] action-name start-time end-time days scale-out-capacity scale-in-capacity
task-name (optional)
The task in your configuration that will contain the new scheduled action configuration. This is optional if you only have 1 scalable task.
action-name
action-name is a name for your schedule. It must be
unique within your entire configuration.
start-time
The starting time of the scheduled action as MM::HH (UTC).
Example: 00:18
end-time
The time to scale back in. Same format as start-time.
days
The the day or days of the week for the scheduled action.
Note: Days should be one of MON,TUE,WED,THU,FRI,SAT or 1-7
Example 1:
Scale out to 4 tasks at 10pm (EDT) for 30 minutes to run a batch job on Friday night.
00:02 30:02 SAT 4/1 4/1
Note that the cron specification is in UTC, hence we run at 2am for 30 minutes on Saturday morning in UTC.
scale-out-capacity
scale-in-capacity
These options represent the scale out and scale in capacities.
Each value should be a tuple separated by '/', ',', ':' or '-'. The first value represents the minimum or maximum capacity for scaling out or in at the specified starting time of schedule action. The second value represents the minimum or maximum capacity for scaling in or out at the ending time of the action.
Example 1:
To scale out to 2 tasks during business hours of 8:30am and 5:30pm and scale in to 1 task during non-business hours (with no metric based scaling policy):
app-FargateStack add-scheduled-action business_hours 30:12 30:21 2/1 2/1
Note that without a scaling policy your minimum and maximum capacities for scaling in and out must be equal.
Example 2:
If your task includes a scaling policy, your scaling policy's max_capacity
must be greater than or equal to the largest maximum capacity of your
scheduled action.
app-FargateStack add-scheduled-action business_hours 30:12 30:21 2/1 3/1
In this case, your scaling policy max_capacity value must be at least
4. You autoscaling: section will look like this:
tasks:
apache:
type: https
autoscaling:
min_capacity: 1
max_capacity: 3
requests: 1000
scale_in_cooldown: 300
scale_out_cooldown: 60
scheduled:
business_hours:
start_time: 30:12
end_time: 30:21
min_capacity: 2/1
max_capacity: 3/1
Note:
Scheduled actions are only for HTTP, HTTPS and daemon tasks. If you need to run a one-shot job at a particular time use a scheduled task.
Reads the configuration file and determines what actions to perform and what resources will be built. Builds resources incrementally and updates configuration file with resource details.
create-stack app-name service-clauses...
Parses a compact, positional CLI grammar and emits a ready-to-edit YAML configuration for your Fargate framework. The command does not create any AWS resources; it only synthesizes a configuration based on the clauses you pass.
Examples:
# One task service
app-fargate create-stack foo task:job image:myrepo:1.2.3
# HTTP service (ALB) + image
app-fargate create-stack foo http:web image:site:2025-08-14 domain:api.example.com
# HTTPS service (ALB + ACM; config only, no deploy)
app-fargate create-stack foo https:web image:site:stable domain:api.example.com
# Scheduled task (EventBridge schedule expression)
app-fargate create-stack foo scheduled:bar 'schedule:cron(0 10 * * * *)' image:helloworld
# Multiple services in one run
app-fargate create-stack foo \
task:ingest image:etl:42 \
scheduled:nightly 'schedule:rate(1 day)' image:etl:42 \
http:api image:rest:latest domain:api.example.com
Each service is introduced by <type>:<name> followed by its required
key:value pairs. You may specify multiple services in one command.
Note: You must start each task definition set with a task type (one of daemon, task, scheduled, http or https).
Valid type values and minimum keys:
environment
environment:RUN_ONCE=1
Sets an environment variable in the task. You can use env: as an
abbreviation for environment:.
task
task:<name> image:<repo[:tag]>
Non-daemon task that can be run on demand.
http
http:<name> image:<repo[:tag]> domain:<fqdn>
ALB-backed HTTP service.
https
https:<name> image:<repo[:tag]> domain:<fqdn>
ALB-backed HTTPS service (certificate discovery/validation is out of scope for this command; see the env checker).
scheduled
scheduled:<name> image:<repo[:tag]> schedule:<expr>
EventBridge-scheduled task. schedule must be a valid cron(...) or
rate(...) expression. Quote it in the shell, for example:
'schedule:cron(0 10 * * * *)'.
Note: You can use task: or scheduled: to indicate a scheduled task
as long as you include a schedule: term.
daemon
daemon:<name>
Long-running service without a load balancer.
image
image:<repo[:tag]>
If image is given as repo[:tag] without a registry host:
The command assumes the image lives in the current account's ECR and will format the Docker reference as:
<account_id>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag>
public.ecr.aws/namespace/image:tag,
docker.io/library/nginx:1.27) are accepted as-is.autoscaling
autoscaling:cpu|request[=value]
For services of type https, http, or daemon, you can enable
and configure autoscaling directly from the command line. This provides a
quick-start method to make your service elastic from the moment it's created.
The autoscaling: keyword accepts a metric and an optional target value:
Enable with a specific target value:
autoscaling:requests=500
autoscaling:cpu=60
This will enable autoscaling and set the target for either ALB requests per task or average CPU utilization.
Enable with default target value:
autoscaling:requests
autoscaling:cpu
If you omit the target value, a sensible default will be used (e.g.,
500 for requests, 60 for CPU).
When the create-stack command sees the autoscaling: keyword, it
will generate a complete autoscaling block in your configuration
file. This block will be populated with safe defaults (min_capacity: 1,
max_capacity: 2), the specified metric, and all other necessary fields,
making it easy to review and customize later. See "AUTOSCALING" for
a full list of configuration options.
waf
waf:true|enabled|default|rule...
For https services, you can enable and configure an AWS Web
Application Firewall (WAF) directly from the command line. This
provides a powerful shortcut to bootstrapping a secure,
production-ready WAF with minimal configuration.
The waf: keyword is highly flexible and accepts several forms:
Enable with defaults:
waf:true
waf:enabled
waf:default
Any of these will enable WAF and apply the default managed rule
bundle, which provides a strong security baseline including
protections against the OWASP Top 10 and SQL injection.
Enable with specific rule sets:
You can specify a comma-separated list of rule set keywords. This allows you to tailor the protection to your application's specific needs from the very first command.
waf:base,php,admin
Enable with bundles and subtractive syntax:
For more complex configurations, you can use pre-configured bundles
and the subtractive syntax (prefixing a keyword with a -) to remove
unwanted rule sets.
waf:all,-windows,-php
When the create-stack command sees the waf: keyword, it will
automatically generate the corresponding waf block in your
fargate-stack.yml file, including enabled: true and the
specified managed_rules. See "Configuring Managed Rules" for a
full list of available keywords and bundles.
For more information see "AWS WAF Support".
Emits YAML to STDOUT that includes:
account, profile, regionapp.name set from the first positional <app-name>domain (for HTTP/HTTPS stacks)tasks map keyed by service <name> with fields such as type,
image, and schedule (when applicable)--route53-profile STR
AWS profile to use when performing Route 53 API calls. Many environments use a separate account for DNS management; this option lets you target that account. If not provided, the tool uses --profile.
This option is only consulted when the command needs Route 53 (for example, HTTP/HTTPS stacks that require hosted zone lookups or record creation).
--dns-profile STR
Alias for --route53-profile.
--region STR
AWS region used when expanding ECR shorthand.
--out FILE
Write YAML to a file instead of STDOUT.
--force
Proceed even if some validations warn (for example, missing ECR repo).
0 on success
non-zero on argument or validation errors
domain: is required at creation time in this shorthand.schedule:... to avoid shell interpretation of parentheses.deploy-service service-name
When you provision an HTTP, HTTPS, or daemon service, the framework sets up all the necessary infrastructure components -- but it does not automatically create and start the ECS service.
Use this command to start the service:
app-FargateStack deploy-service service-name
If you want to start multiple tasks for the service, you can include a count argument:
app-FargateStack deploy-service service-name 2
delete-daemon task-name
Deletes the AWS resources associated with a task of type
daemon. Consider removing the service
("remove-service") or stopping the service
("stop-service") if you do not want to delete the actual
resources.
See "Notes on Deletion of Resources" for additional details.
delete-scheduled-task task-name
Deletes the AWS resources associated with a task of type task that
includes a schedule: key.
See "Notes on Deletion of Resources" for additional details.
delete-task task-name
Deletes the AWS resources associated with a task of type task.
See "Notes on Deletion of Resources" for additional details.
Deletes the AWS resources associated with a task of type http or https.
If the Application Load Balancer (ALB) used by the service was
provisioned by App::FargateStack, it will be automatically
deleted. However, if the ALB was discovered but not created by
App::FargateStack, it will be preserved. In that case, only the listener
rules provisioned by App::FargateStack will be removed.
This command will also not delete any ACM certificate that was
provisioned by App::FargateStack.
See "Notes on Deletion of Resources" for additional details.
Removes all resources provisioned by App::FargateStack. This command
will confirm deletion before removing any resources. Use --force to
prevent confirmation. Use --confirm-all to confirm deletion of
every resource.
After this command is executed a skeleton of the tasks will
remain. You can run plan again and then apply to reprovision
the stack.
Use the --dryrun and --confirm-all options to review the
resources to be removed.
disable-scheduled-task task-name
Use this command to disable a scheduled task.
If you omit task-name, the command will attempt to determine the
target task selecting the task of type task with a defined
schedule: key but only if exactly one such task is defined in
your configuration file.
enable-scheduled-task task-name
Use this command to enable a scheduled task.
If you omit task-name, the command will attempt to determine the
target task selecting the task of type task with a defined
schedule: key but only if exactly one such task is defined in
your configuration file.
list-tasks [stopped]
Lists running or stopped tasks and outputs a table of information about the tasks.
Task Name
Task Id
Status
Memory
CPU
Start Time
Elapsed Time
Stopped Reason
list-zones domain-name
This command will list the hosted zones for a specific domain. The
framework automatically detects the appropriate hosted zone for your
domain if the zone_id: key is missing from your configuration when
you have an HTTP or HTTPS task defined.
Example:
app-FargateStack list-zones --profile prod
logs start-time end-time
To view your log streams use the logs command. This command will
display the logs for the most recent log stream in the log group. By
default the start time is the time of the first event.
--log-wait to continuously poll the log stream.--no-log-time if your logs already have timestamps and do
not want to see CloudWatch timestamps. This is useful when you are
logging time in your time zone and do not want to be confused seeing
times that don't line up.start-time can be a "Nh", "Nm", "Nd" where N is an integer
and h=hours ago, m=minutes ago and d=days ago.start-time and end-time can be "mm/dd/yyyy hh:mm:ss" or just "mm/dd/yyyy"end-time must always be a date-time string.Reads the configuration file and determines what actions to perform and what resources will be built. Only updates configuration file with resource details but DOES NOT build them.
redeploy service-name
Forces a new deployment of the specified ECS service without registering a new task definition. This triggers ECS to stop the currently running task and launch a new one using the same task definition revision.
If you omit service-name, the command will attempt to determine the
target service by selecting the task of type daemon, http, or
https, but only if exactly one such service is defined in your
configuration file.
If the task definition references an image by tag (such as :latest), this
command ensures ECS re-pulls the image from ECR at deployment time. This allows
you to deploy a newly pushed image without needing to create a new revision of
the task definition.
This command is especially useful when:
:latest)Note that if your task definition references an image by digest
(e.g. @sha256:...), ECS will continue to use that exact image. In that case,
you must register a new task definition to update the image.
For best results, use this command as a shortcut to avoid
register-task, update-service steps and only when your service's
task definition uses an image tag that can be re-resolved, such as
:latest or a CI-generated version tag.
register-task-definition task-name
Creates a new task definition revision in ECS for the specified task.
Under normal circumstances, you should not need to run this command
manually. Task definitions are automatically registered when you
execute plan or apply.
This command is provided for exceptional cases where you need to force a new revision using a previously generated task definition file.
Warning: You should not manually modify the generated file
(taskdef-{task-name}.json), as doing so may cause
App::FargateStack to lose track of your task's configuration.
remove-service service-name
Deletes a running ECS service without removing any of the underlying AWS resources.
If you simply want to stop the service temporarily, use the
stop-service command instead.
This command does not delete associated infrastructure such as the target group, security group, or load balancer listener rules. To delete those resources, see "delete-daemon" or "delete-http-service", depending on the task type.
run-task task-name
Launches a one-shot Fargate task. By default, the command waits for the
task to complete and streams the task's logs to STDERR. Use the --no-wait
option to launch the task and return immediately.
When you register a task definition, ECS records the image digest of the
image specified in your configuration file. If you later push a new image
tagged with the same name (e.g., latest) without updating the task
definition, ECS will continue to use the original image digest.
To detect this kind of drift, app-FargateStack records the image digest
at the time of task registration and compares it to the current digest
associated with the tag (typically latest) at runtime.
If the digests do not match, the default behavior is to abort execution
and warn you about the mismatch. To override this safety check and proceed
anyway, use the --force option.
state config-name
You can use this command to switch the default configuration that
app-FargateStack will use when run without arguments.
The default configuration controls which task profile, region, and configuration file are considered "current." This allows you to run commands without repeatedly specifying the same options.
This command will output the table below that shows the currently active defaults:
.--------------------------------------------------------------------------------------------------.
| Current Defaults: http-test |
+---------+-------------+-----------+-------------------------------------------------+------------+
| Profile | DNS Profile | Region | Config | Max Events |
+---------+-------------+-----------+-------------------------------------------------+------------+
| sandbox | prod | us-east-1 | /home/rlauer/git/App-FargateStack/http-test.yml | 5 |
'---------+-------------+-----------+-------------------------------------------------+------------'
status service-name
Displays the status of a running service and its most recent event messages in tabular form.
If you omit service-name, the command will attempt to determine the
target service by selecting the task of type daemon, http, or
https, but only if exactly one such service is defined in your
configuration file.
Use the --max-events option to control how many recent events are shown.
The default is 5.
stop-task task-arn|task-id
Stops a running task. To get the task id, use the list-tasks
command.
stop-service service-name
Stops a running service by setting its desire count to 0.
If you omit service-name, the command will attempt to determine the
target service by selecting the task of type daemon, http, or
https, but only if exactly one such service is defined in your
configuration file.
start-service service-name [count]
Start a service. count is the desired count of tasks. The default
count is 1.
If you omit service-name, the command will attempt to determine the
target service by selecting the task of type daemon, http, or
https, but only if exactly one such service is defined in your
configuration file.
Displays a table that summarizes your stack resources.
update-policy
Forces the framework to re-evaluate resources and align the
policy. Will not apply changes in --dryrun mode. Under normal
circumstances you should not need to run this command, however if you
find that your Fargate policy lacks permissions for resources you have
configure, this will make sure that all configured resources are
included in your policy.
If update-policy identifies a need to update your role policy, you
can view the changes before they are applied by running the plan
command at the trace log level.
app-FargateStack --log-level trace plan
update-service [service-name]
Updates an ECS service's configuration to use the latest registered task definition. This is the primary command for deploying any changes to your application, including new container images, environment variables, or resource allocations.
When an ECS service is launched, it is "pinned" to a specific revision of a task definition (e.g., my-task:9). If you later push a new container image or change the task's configuration in your configuration file, the running service will not automatically pick up those changes.
This command is the essential final step in the deployment process.
When to use update-service vs. redeploy
While both commands can result in a new deployment, they serve different purposes:
Use update-service when you have made any change to your
configuration file that affect the task definition. This is the
correct command for deploying a new image, adding environment
variables, injecting secrets, changing CPU/memory, or adding EFS mount
points. The workflow is:
Update your configuration file.
Run app-FargateStack register-task-definition task-name
Run app-FargateStack update-service task-name
Use redeploy as a shortcut only when you have pushed a new image using
the same tag (e.g., :latest) and have made no other configuration
changes. redeploy forces a new deployment using the existing task
definition, which is simpler but will not apply any other updates.
The status command can help you detect drift by showing if the running task definition is out of sync with your latest configuration.
update-target task-name
Updates an EventBridge rule and rule target. For tasks of type "task"
(typically scheduled tasks) when you change the schedule the rule must
be deleted, re-created and associated with the target task. This
command will detect the drift in your configuration and apply the
changes if not in --dryrun mode.
Outputs the current version of App::FargateStack.
plan against the skeleton, avoiding the need
to recreate it from scratch.App::FargateStack does not delete ECR images associated with tasks.App::FargateStack will not be
deleted.One of the most common questions when managing a stack is, "I changed X, what command(s) do I need to run now?" This guide provides a quick-reference matrix to help you choose the correct workflow for the most common changes.
Find the change you made in the "Change Description" column and follow the row across to see which commands are required. Commands should be run in order from left to right.
+---------------------------------------------+---------+---------+----------+----------+
| Change Description | apply | register| update- | redeploy |
| | | -task | service | |
+---------------------------------------------+---------+---------+----------+----------+
| Updated container image (new tag/digest) | | X | X | |
|---------------------------------------------+---------+---------+----------+----------|
| Updated container image (same :latest tag) | | | | X |
|---------------------------------------------+---------+---------+----------+----------|
| Added/changed environment variables | | X | X | |
|---------------------------------------------+---------+---------+----------+----------|
| Added/changed secrets | X | X | X | |
|---------------------------------------------+---------+---------+----------+----------|
| Added/changed CPU, memory, or size | | X | X | |
|---------------------------------------------+---------+---------+----------+----------|
| Changed a scheduled task's cron/rate | X | | | |
|---------------------------------------------+---------+---------+----------+----------|
| Added a new S3 bucket or SQS queue | X | X | X | |
|---------------------------------------------+---------+---------+----------+----------|
| Added or changed an EFS mount point | X | X | X | |
+---------------------------------------------+---------+---------+----------+----------+
plan is Your Best Friend: Before running apply or any command that
makes changes, it is always a good practice to run app-FargateStack
plan first. This will give you a dry-run preview of the changes and
help you catch any configuration errors.A CloudWatch log group is automatically provisioned for each application stack. By default, the log group name is /ecs/<application-name>, and log streams are created per task.
For example, given the following configuration:
app:
name: my-stack
...
tasks:
apache:
type: https
The framework will:
By default, the log group is set to retain logs for 14 days if
retention_days is not specified. You can override this by
specifying a custom retention period using the retention_days key
in the task's log_group section:
log_group:
retention_days: 30
You can customize the log group name by setting the name in
the log_group: section (not recommended).
log_group:
retention_days: 14
name: /ecs/my-stack
You can change the retention period by updating the
configuration file and re-running apply.
retention_days
entry in your configuration file.This framework uses a single IAM role for all tasks defined within an application stack. The assumption is that services within the stack share a trust boundary and operate on shared infrastructure. This simplifies IAM management while maintaining strict isolation between stacks.
IAM roles and policies are automatically created based on your configuration. Only the minimum required permissions are granted. For example, if your configuration defines an S3 bucket, the ECS task role will be permitted to access only that specific bucket - not all buckets in your account. The task IAM policy is updated when new resources are added to the configuration file.
The task execution role name and role policy name are found under the
role: key in the configuration. The task role is found under the
task_role: key. Role names and role policy names are automatically
fabricated for you from the name you specified under the app: key.
It's important to understand that App::FargateStack provisions two
distinct IAM roles for your service. The Task Role, which is detailed
above, grants your application the specific permissions it needs to
interact with other AWS services like S3 or SQS. In addition, the
framework also creates a Task Execution Role. This second role is used
by the Amazon ECS container agent itself and grants it permission to
perform essential actions, such as pulling container images from ECR
and sending logs to CloudWatch. You typically won't need to modify the
Task Execution Role, as the framework manages its permissions
automatically.
The decision to use a single IAM task role for an entire
App::FargateStack stack is a classic trade-off between simplicity
and security. While it successfully simplifies management, it does so
by moving away from the security best practice of least privilege.
The primary advantage of this approach is its simplicity, which aligns with the framework's goal of abstracting away AWS complexity.
Simplified Management
Users don't have to define and manage multiple, near-identical IAM roles for tasks that often share resources within the same application stack. This reduces configuration boilerplate and mental overhead.
Avoids Role Proliferation
In a microservices architecture, creating a separate role for every single task can lead to a large number of IAM roles that are difficult to track and audit. A single-stack role prevents this "IAM sprawl."
Automatic Policy Updates
The framework automatically updates the single role's policy when new resources are added, ensuring permissions are always in sync with the configuration without manual intervention.
The main disadvantage is that this design violates the principle of least privilege, a core tenant of cloud security.
Over-Provisioned Permissions
If one task in the stack needs access to an S3 bucket and another needs access to an SQS queue, both tasks get permissions for both the bucket and the queue. The SQS-processing task now has unnecessary access to S3, and vice-versa.
Increased Blast Radius
If a vulnerability is exploited in a single task (e.g., the one that only needs SQS access), the attacker gains the permissions of the entire stack. They could potentially access or delete data in the S3 bucket, even though the compromised task had no legitimate reason to access it.
Think of it like giving every employee in an office building a master key. It's much simpler than managing individual keys for each room, but if one person's key is lost or stolen, the entire building is at risk.
The single-role design makes App::FargateStack easier to use,
especially for smaller applications or for teams that prioritize rapid
development over granular security. It successfully meets its goal of
simplifying IAM management for the end-user.
However, this simplicity comes at the cost of a weaker security posture. For environments with strict security requirements or for larger, more complex applications with diverse tasks, this approach could be a significant risk. A more secure, albeit more complex, approach would be to define permissions at the task level, ensuring each task receives only the permissions it absolutely needs to perform its function.
A security group is automatically provisioned for your Fargate
cluster. If you define a task of type http or https, the
security group attached to your Application Load Balancer (ALB) is
automatically authorized for ingress to your Fargate task. This is a
rule allowing ALB-to-Fargate traffic.
EFS volumes are defined per task and mounted according to the task definition. This design provides fine-grained control over EFS usage, rather than treating it as a global, stack-level resource.
Each task that requires EFS support must include both a volume and mountPoint configuration. The ECS task role is automatically updated to allow EFS access based on your specification.
To specify EFS support in a task:
efs:
id: fs-1234567b
mount_point: /mnt/my-stack
path: /
readonly:
Acceptable values for readonly are "true" and "false".
id:
The ID of an existing EFS filesystem. The framework does not provision the EFS, but will validate its existence in the current AWS account and region.
mount_point:
The container path to which the EFS volume will be mounted.
path:
The path on the EFS filesystem to map to your container's mount point.
readonly:
Optional. Set to true to mount the EFS as read-only. Defaults to
false.
readonly: in your task's
efs: section to "true" if only want read support.The App::FargateStack framework defines your application stack
using a YAML configuration file. This file describes your
application's services, their resource needs, and how they should be
deployed. Then configuration is updated whenever your run plan or
apply.
The fastest way to get up and running with App::FargateStack is to
use the create-stack command to generate a configuration file,
inspect the deployment plan, and then apply it.
AWS CLI - install and configure aws
Perl >= 5.16.3
NOTE: If you use App::FargateStack-docker you only need
Docker. You only need Perl and the required Perl modules if you
install App::FargateStack from the CPAN repository.
See https://app-fargatestack.tbcdevelopmentgroup.com for more detailed installation instructions.
Docker - required for building and pushing container images or
for using the app-FargateStack-docker implementation
An appropriately configured AWS account
You can check your current environment's suitablility for building
Fargate applications using the app-FargateChecker script.
app-FargateStack-checker --profile my-profile
app-FargateStack-checker-docker --profile my-profile
Your profile should allow creation of the resources you wish to provision, including the ability to create IAM roles and policies. In general, your profile should include the AdministratorAccess policy.
A Route53 Hosted Zone if you are planning to deploy HTTP or HTTPS services.
First, generate a minimal YAML configuration file. The create-stack
command provides a shorthand syntax to do this. You only need to
provide an overall application name, a service type, a service name,
and the container image to use.
This command shown below will create a file named my-stack.yml in your
current directory. Make sure you have your AWS profile configured in
your environment or pass it using the --profile option.
export AWS_PROFILE=my-profile
app-FargateStack create-stack my-stack daemon:my-stack-daemon image:my-stack-daemon:latest
This will produce a configuration stub that looks something like this:
app:
name: my-stack
profile: my-profile
tasks:
my-stack-daemon:
image: my-stack-daemon:latest
type: daemon
This file contains the key pieces of information you provided: the application name, the task name, and the image to use.
Next, run the plan command. This is a crucial step that acts as a
dry run. The framework will:
Update your configuration file with the discovered values and sensible defaults.
app-FargateStack plan
After this command completes, your my-stack.yml file will be fully
populated with all the information needed to provision your stack.
Once you have reviewed the plan and are satisfied with the proposed
changes, run the apply command. This will execute the plan and
create all the necessary AWS resources.
app-FargateStack apply
The apply command creates all the necessary infrastructure, but
it does not start your service. This separation allows you to manage
your infrastructure and your application's runtime state
independently.
To create the ECS service and start your container, use the
deploy-service command.
app-FargateStack deploy-service my-stack-daemon
By default, this will start one instance of your task. To check its
status, use the status command:
app-FargateStack status my-stack-daemon
And to stop the service, simply run:
app-FargateStack stop-service my-stack-daemon
To restart a stopped service, run:
app-FargateStack start-service my-stack-daemon
You can define queues, buckets and secrets for your stack and for your individual tasks. The matrix below details where you configure those resources and their scope.
+-----------------------+---------+---------+
| Resource | Level | Scope |
+-----------------------+---------+---------+
| SQS Queues | stack | stack |
| S3 Buckets | stack | stack |
| Secrets | task | stack* |
| EFS mounts | task | task |
| Environment Variables | task | task |
+-------------+---------+---------+---------+
* Secrets are defined per-task but the IAM permissions to access them are granted at the stack level via a shared task role.
A task definition is created for each task defined in your
configuration file, however any task can access resources defined at
the stack level since they will all use the same task IAM role created
by App::FargateStack.
If you do not specify a vpc_id in your configuration, the framework
will attempt to locate a usable VPC automatically.
A VPC is considered usable if it meets the following criteria:
If no eligible VPCs are found, the process will fail with an error. If multiple
eligible VPCs are found, the framework will abort and list the candidate VPC IDs.
You must then explicitly set the vpc_id: in your configuration to resolve
the ambiguity.
If exactly one eligible VPC is found, it will be used automatically, and a warning will be logged to indicate that the selection was inferred.
If no subnets are specified in the configuration, the framework will query all subnets in the selected VPC and categorize them as either public or private.
The task will be placed in a private subnet by default. For this to succeed, your VPC must have at least one private subnet with a route to a NAT Gateway, or have appropriate VPC endpoints configured for ECR, S3, STS, CloudWatch Logs, and any other services your task needs.
If subnets are explicitly specified in your configuration, the framework will validate them and warn if they are not reachable or are not usable for Fargate tasks.
The framework places each task's ENI into exactly one subnet, which fixes that task in a single AZ. A service can span multiple AZs by listing subnets from at least two AZs.
What the framework does:
Prefers private subnets
If private subnets are defined in the configuration, tasks are placed there. If no private subnets are defined, the framework falls back to public subnets.
Aligns ALB AZs with task placement
When a load balancer is used, the framework enables the ALB in the same AZ set it selects for tasks (best practice). This is for resilience and to avoid unnecessary cross-AZ hops; it is not a hard technical requirement.
Requires two subnets
The configuration must specify at least two subnets in different AZs. If subnets are not specified, the framework attempts to discover them, but still requires at least two usable subnets (either both private or both public). If fewer than two are available, it errors with guidance.
Notes on internet access and ALBs:
Internet-facing ALB
An internet-facing ALB must be created in public subnets. Tasks may (and usually should) remain in private subnets behind it.
Egress from private subnets
For image pulls and outbound calls, use either a NAT Gateway in each AZ or VPC endpoints for ECR (api and dkr) and S3.
Egress from public subnets
If tasks are placed in public subnets without endpoints or NAT, they
require assignPublicIp=ENABLED to reach ECR/S3.
At minimum, your configuration must include the following:
app:
name: my-stack
tasks:
my-task:
image: my-image
type: daemon | task | http | https
For task types http or https, you must also specify a domain name:
domain: example.com
The framework will expand and update your configuration file with default values as needed. Here is the full schema outline. All keys are optional unless otherwise noted:
---
account:
alb:
arn:
name:
port:
type:
app:
name: # required
version:
certificate_arn:
cluster:
arn:
name:
default_log_group:
domain: # required for http/https tasks
id:
last_updated:
region:
task_role:
arn:
name:
policy_name:
role:
arn:
name:
policy_name:
route53:
profile:
zone_id:
security_groups:
alb:
group_id:
group_name:
fargate:
group_id:
group_name:
subnets:
private:
public:
tasks:
my-task:
arn:
cpu:
family:
image: # required
log_group:
arn:
name:
retention_days:
memory:
name:
size:
target_group_arn:
target_group_name:
task_definition_arn:
type: # required (daemon, task, http, https)
vpc_id:
To simplify task configuration, the framework supports a shorthand key called
size that maps to common CPU and memory combinations supported by Fargate.
If specified, the size parameter should be one of the following profile names:
tiny => 256 CPU, 512 MB memory
small => 512 CPU, 1 GB memory
medium => 1024 CPU, 2 GB memory
large => 2048 CPU, 4 GB memory
xlarge => 4096 CPU, 8 GB memory
2xlarge => 8192 CPU, 16 GB memory
When a size is provided, the framework will automatically populate the
corresponding cpu and memory values in the task definition. If you
manually specify cpu or memory alongside size, those manual values
will take precedence and override the defaults from the profile.
Important: If you change the size after an initial deployment, you should
remove any manually defined cpu and memory keys in your configuration.
This ensures that the framework can correctly apply the new profile values
without conflict.
If neither size, cpu, nor memory are provided, the framework will infer
a sensible default size based on the task type. For example:
- "http" or "https" => "medium"
- "task" => "small"
- "task" + schedule => "medium"
- "daemon" => "medium"
This behavior helps minimize configuration boilerplate while still providing sane defaults.
The Fargate stack framework allows you to define environment variables for each task. These variables are included in the ECS task definition and made available to your container at runtime.
Environment variables are specified under the environment: key within the task
configuration.
task:
apache:
environment:
ENVIRONMENT: prod
LOG_LEVEL: info
DEBUG_MODE: 0
Each key/value pair will be passed to the container as an environment variable.
Environment variable values are treated literally; shell-style
expressions such as ${VAR} are not interpolated. If you need dynamic
values, populate them explicitly in the configuration or use the
secrets: block for sensitive data.
This mechanism is ideal for non-sensitive configuration such as runtime flags, environment names, or log levels.
Avoid placing secrets (such as passwords, tokens, or private keys) directly in the
environment: section. That mechanism is intended for non-sensitive configuration
data.
To securely inject secrets into the task environment, use the secrets: section
of your task configuration. This integrates with AWS Secrets Manager and ensures
secrets are passed securely to your container.
To inject secrets into your ECS task from AWS Secrets Manager, define a secrets:
block in the task configuration. Each entry in this list maps a Secrets Manager
secret path to an environment variable name using the following format:
/secret/path:ENV_VAR_NAME
Example:
task:
apache:
secrets:
- /my-stack/mysql-password:DB_PASSWORD
This configuration retrieves the secret value from /my-stack/mysql-password
and injects it into the container environment as DB_PASSWORD.
Secrets are referenced via their ARN using ECS's native secrets mechanism, which securely injects them without placing plaintext values in the task definition.
Avoid placing secrets in the environment: block. That block is for non-sensitive
configuration values and exposes data in plaintext.
Use clear, descriptive environment variable names (e.g., DB_PASSWORD, API_KEY)
and organize your Secrets Manager paths consistently with your stack naming.
The Fargate stack framework supports configuring and provisioning a single AWS SQS queue, including an optional dead letter queue (DLQs).
A queue is defined at the stack level and is accessible to all tasks and services within the same stack. IAM permissions are automatically scoped to include only the explicitly configured queue and its associated DLQ (if any).
Only one queue and one optional DLQ may be configured per stack.
At minimum, a queue requires a name:
queue:
name: fu-man-q
If you define max_receive_count in the queue configuration, a DLQ
will be created automatically. You can optionally override its name
and attributes using the top-level dlq key:
queue:
name: fu-man-q
max_receive_count: 5
dlq:
name: custom-dlq-name
If you do not specify a dlq.name, the framework defaults to appending -dlq to
the main queue name (e.g., fu-man-q-dlq).
=head2 Customizing IAM Permissions
While the framework automatically grants basic permissions, you can
precisely control the IAM policy for each queue using the optional permissions
object. This provides a flexible, declarative way to define permissions without
writing raw JSON.
If the permissions key is omitted, permissions will default to the consumer
profile.
Both queue and dlq objects can have their own permissions block.
permissions Objectprofile (Optional, Default: consumer)
Specifies a base set of permissions. The available profiles are:
consumer
(Default) Grants sqs:ReceiveMessage, sqs:DeleteMessage, and
sqs:ChangeMessageVisibility. Ideal for the main worker queue.
producer
Grants sqs:SendMessage and sqs:GetQueueAttributes. The recommended
profile for a DLQ if you wish to apply the Principle of Least Privilege.
admin
Grants sqs:* for full administrative access to the queue.
none
Grants no permissions by default, requiring you to specify all desired
actions via the include key.
include (Optional)
An array of specific IAM action strings to add to the base profile. This is useful for granting extra permissions not included in the standard profile.
exclude (Optional)
An array of specific IAM action strings to remove from the base profile. This is useful for creating a more restricted set of permissions than the default profile provides.
Here is a full example demonstrating a main queue and a DLQ with custom permissions.
If not specified, the framework applies default values to match AWS's standard SQS behavior:
queue:
name: fu-man-q
visibility_timeout: 30
delay_seconds: 0
receive_message_wait_time_seconds: 0
message_retention_period: 345600
maximum_message_size: 262144
max_receive_count: 5 # triggers DLQ creation
dlq:
visibility_timeout: 30
delay_seconds: 0
receive_message_wait_time_seconds: 0
message_retention_period: 345600
maximum_message_size: 262144
A dead letter queue is not a special type - it is simply another queue used to receive messages that have been unsuccessfully processed. It is modeled as a standalone queue and defined at the top level of the stack configuration.
The dlq block is defined at the same level as queue, not nested within it.
If no overrides are provided, DLQ attributes default to AWS attribute defaults.
Adding a new queue to an existing stack will not only create the queue, but also update the IAM policy associated with your stack to include permissions for the newly defined queue and DLQ (if applicable).
The Fargate stack framework allows you to schedule container-based jobs using AWS EventBridge. This is useful for recurring tasks like report generation, batch processing, database maintenance, and other periodic workflows.
A scheduled task is defined like any other task, using type: task, and
adding a schedule: key in AWS EventBridge cron format.
To schedule a task, add a schedule: key to your task definition. The
value must be a valid AWS cron expression, such as:
cron(0 2 * * ? *) # every day at 2:00 AM UTC
Example:
tasks:
daily-report:
type: task
image: report-runner:latest
schedule: cron(0 2 * * ? *)
Note: All cron expressions are interpreted in UTC.
The framework will automatically create an EventBridge rule tied to the task definition. When triggered, it will launch a one-off Fargate task based on the configuration. The EventBridge rule is named using the pattern "<task>-schedule".
All scheduled tasks support environment variables, secrets, and other standard task features.
You can run a scheduled (or unscheduled) task manually at any time using:
app-FargateStack run-task task-name
By default, this will:
--no-wait is passed)This is ideal for debugging, re-running failed jobs, or triggering occasional maintenance tasks on demand.
A task of type daemon is launched as a long-running ECS service
and benefits from restart policies and availability guarantees.
A task of type task is run using run-task and may run once,
forever, or periodically - but it will not be automatically restarted
if it fails.
The Fargate stack framework supports creating a new S3 bucket or using an existing one. The bucket can be used by your ECS tasks and services, and the framework will configure the necessary IAM permissions for access.
By default, full read/write access is granted unless you specify restrictions (e.g., read-only or path-level constraints). In this model, no bucket policy is required or modified.
Note: Full access includes s3:GetObject, s3:PutObject, s3:DeleteObject, and s3:ListBucket. Readonly access is limited to s3:GetObject and s3:ListBucket.
You define a bucket in your configuration like this:
bucket:
name: my-app-bucket
By default, this grants full read/write access to the entire bucket via the IAM role attached to your ECS task definition.
You can limit access to a subset of the bucket using the readonly: and
paths: keys:
bucket:
name: my-app-bucket
readonly: true
paths:
- public/*
- logs/*
This will:
s3:GetObject and s3:ListBucket permissionsThe paths: values are interpreted as S3 key prefixes and inserted
directly into the role policy.
If you specify readonly: true but omit paths:, read-only access will
apply to the entire bucket. If you omit both keys, full read/write access
is granted.
Bucket access is enforced exclusively through IAM role permissions. The framework does not modify or require an S3 bucket policy. This keeps your configuration simpler and avoids potential conflicts with externally managed bucket policies.
If you reference an existing bucket not created by the framework, be aware that the bucket's own policy may still restrict access.
In particular:
Deny will override that and block accessTo avoid surprises, ensure that any bucket policy on an external bucket permits access from the IAM role you're configuring.
To create a Fargate HTTP service set the type: key in your task's
configuration section to "http" or "https".
The task type ("http" or "https") determines:
Your domain is managed in Route 53 and your profile can create Route 53 record sets.
Note: If your domain is managed in a different AWS account, set a
separate profile: value in the route53: section of the
configuration file. Your profile should have sufficient permissions
to manage Route 53 recordsets.
Your Fargate task will be deployed in a private subnet and will listen on port 80.
When you set your task type to "http" or "https" a default architecture depicted below will be provisioned.
(optional)
+------------------+
| Internet Client |
+--------+---------+
|
[only if ALB is external]
|
+------------v--------------+
| Route 53 Hosted Zone |
| Alias: myapp.example.com |
| --> ALB DNS Name |
+----------+----------------+
|
+----------v----------+
| Application Load |
| Balancer (ALB) |
| [internal or |
| internet-facing] |
| |
| Listeners: |
| - Port 80 |
| - Port 443 w/ TLS |
| + ACM Cert |
| (TLS/SSL) |
| [if external] |
+----------+----------+
|
+------v-------+
| Target Group |
+------+-------+
|
+-------v---------+
| ECS Service |
| (Fargate Task) |
+-------+---------+
|
+---------v----------+
| VPC Private Subnet |
+--------------------+
This default architecture provides a repeatable, production-ready deployment pattern for HTTP services with minimal configuration.
For HTTP services, you set the task type to either "http" or "https" (these are the only options that will trigger a task to be configured for HTTP services). The table below summarizes the configurations by task type.
+-------+----------+-------------+-----------+---------------+
| Type | ALB type | Certificate | Port | Hosted Zone |
+-------+----------+-------------+-----------+---------------+
| http | internal | No | 80 | private |
| https | external | Yes | 443 | public |
| | | | 80 => 443 | |
+-------+----------+-------------+-----------+---------------+
NOTE: You must provide a domain name for both an internal and external facing HTTP service. This also implies you must have a both a private and public hosted zone for your domain.
Your task type will also determine which type of subnet is required
and where to search for an existing ALB to use. If you want to prevent
re-use of an existing ALB and force the creation of a new one use the
--create-alb option when you run your first plan.
In your initial configuration you do not need to specify the subnets or the hosted zone id. The framework will discover those and report if any required resources are unavailable. If the task type is "https", the script looks for a public zone, public subnets and an internet-facing ALB otherwise it looks for a private zone, private subnets and an internal ALB.
If the task type is "https" and no ACM certificate currently exists for your domain, the framework will automatically provision one. The certificate will be created in the same region as the ALB and issued via AWS Certificate Manager. If the certificate is validated via DNS and subsequently attached to the listener on port 443.
For external-facing apps, a separate listener on port 80 is
created. It forwards traffic to port 443 using a default redirect rule
(301). If you do not want a redirect rule, set the redirect_80: in
the alb: section to "false".
If you want your internal application to listen on a port other than
80, set the port: key in the alb: section to a new port
value.
app:
name: http-test
domain: http-test.example.com
task:
apache:
type: http
image: http-test:latest
Based on this minimal configuration app-FargateStack will enrich
the configuration with appropriate defaults and proceed to provision
your HTTP service.
To do that, the framework attempts to discover the resources required for your service. If your environment is not compatible with creating the service, the framework will report the missing resources and abort the process.
Given this minimal configuration for an internal ("http") or external ("https") HTTP service, discovery entails:
Note: Discovery of these resources is only done when they are
missing from your configuration. If you have multiple VPCs for example
you can should explicitly set vpc_id: in the configuration to
identify the target VPC. Likewise you can explicitly set other
resource configurations (subnets, ALBs, Route 53, etc).
Resources are provisioned and your configuration file is updated
incrementally as app-FargateStack compares your environment to the
environment required for your stack. When either plan or
apply complete your configuration is updated giving you complete
insight into what resources were found and what resources will be
provisioned. See CONFIGURATION for complete details on resource
configurations.>
Your environment will be validated against the criteria described below.
You have at least 2 private subnets available for deployment
Technically you can launch a task with only 1 subnet but for services behind an ALB Fargate requires 2 subnets.
When you create a service with a load balancer, you must specify two or more subnets in different Availability Zones. - AWS Docs
You have a hosted zone for your domain of the appropriate type (private for type "http", public for type "https")
As discovery progresses, existing and required resources are logged and your configuration file is updated. If you are NOT running in dryrun mode, resources will be created immediately as they are discovered to be missing from your environment.
When you provision an HTTP service, whether or not it is secure, the service will placed behind an application load balancer. Your Fargate service is created in private subnets, so your VPC must contain at least two private subnets. Your load balancer can either be internally or externally facing.
By default, the framework looks for and will reuse a load balancer
with the correct scheme (internal or internet-facing), in a subnet
aligned with your task type. The ALB will be placed in public subnets
if it is internet-facing. You can override that behavior by either
explicitly setting the ALB arn in the alb: section of the
configuration or pass --create-alb when you run our plan and apply.
If no ALB is found or you passed the --create-alb option, a new ALB
is provisioned. When creating a new ALB, app-FargateStack will also
create the necessary listeners and listener rules for the ports you
have configured.
While it is possible to avoid the use or the creation of a load balancer for your service, the framework forces you to use one for at least two reasons. Firstly, the IP address of your service may not be stable and is not friendly for development or production purposes. The framework is, after all trying its best to promote best practices while preventing you from having to know how all the sausage is made.
Secondly, it is almost guaranteed that you will eventually want a domain name for your production service - whether it is an internally facing microservice or an externally facing web application.
Creating an alias in Route 53 for your domain pointing to the ALB ensures you don't need to update application configurations with the service's dynamic IP address. Additionally, using a load balancer allows you to create custom routing rules to your service. If you want to run multiple tasks for your service to support handling more traffice a load balancer is required.
With those things in mind the framework automatically uses an ALB for HTTP services and creates an alias record (A) for your domain for both internal and external facing services.
For external-facing HTTPS services, App::FargateStack can automate
the creation and association of an AWS Web Application Firewall (WAF)
to provide an essential layer of security. This protects your
application from common web exploits and bots that could affect
availability or compromise security.
The framework follows a "Hybrid Management Model" for WAF, designed to provide a secure, sensible baseline out-of-the-box while giving you full control over fine-grained rule customization.
To enable WAF, simply add a waf block with enabled: true to your
alb configuration:
alb:
# ... existing alb configuration ...
waf:
enabled: true
To simplify configuration, App::FargateStack uses a keyword-based
system for enabling AWS Managed Rule Groups. You can specify a list of
keywords under the managed_rules key in your waf configuration.
If the managed_rules key is omitted, the framework will apply the
default bundle, which provides a strong and cost-effective security
baseline.
waf:
enabled: true
managed_rules: [linux-app, admin, -php]
The framework supports both individual rule sets and pre-configured
"bundles" for common application types. It also supports a subtractive
syntax (prefixing a keyword with a -) to remove rule sets from a
bundle.
AWSManagedRulesCommonRuleSet, AWSManagedRulesAmazonIpReputationList, and AWSManagedRulesKnownBadInputsRuleSet.AWSManagedRulesAdminProtectionRuleSet).AWSManagedRulesSQLiRuleSet).base and sql. This is the recommended starting point for most applications.default and linux.default, linux, and wordpress.default and windows.On the first apply run with WAF enabled, the framework will perform
a one-time bootstrap:
web-acl.json file in your project
directory. This file contains the complete definition of your Web ACL,
including the rules generated from your managed_rules keywords.aws wafv2 create-web-acl to create a new Web ACL.aws wafv2 associate-web-acl to link the new Web ACL to
your Application Load Balancer.web-acl.json file.waf block in your fargate-stack.yml is updated to reflect
the bootstrapped state. If the managed_rules key was not present,
it will be added with the default value of [default].After the initial creation, you take full control of the rules. To
add, remove, or modify rules, you simply edit the web-acl.json file
directly.
On subsequent runs of apply, App::FargateStack will:
web-acl.json file.This model gives you the best of both worlds: the "minimal configuration, maximum results" of a secure default, and the full "transparent box" control to customize your security posture as your application's needs evolve.
The framework includes robust safety checks to prevent accidental data
loss. If it detects that the Web ACL has been modified in the AWS
Console and you have also modified your local web-acl.json file,
it will detect the state conflict, refuse to make any changes, and
provide a clear error message with instructions on how to resolve it.
The default WAF configuration is designed to provide a strong security
baseline while remaining cost-effective. When you enable WAF without
specifying any managed_rules, the framework applies the default
bundle, which includes the base and sql rule sets.
The approximate monthly cost for this default configuration is ~$9.00 per month, plus per-request charges.
The cost is broken down as follows:
default bundle (3 in 'base', 1 in 'sql').Warning: Enabling the premium rule set will incur significant
additional monthly and per-request fees for services like Bot Control
and Account Takeover Prevention. Always review the AWS WAF
pricing page before enabling
premium features.
For services that experience variable load, such as HTTP applications or
background job processors, App::FargateStack can automate the process of
scaling the number of running tasks up or down to meet demand. This ensures
high availability during traffic spikes and saves costs during quiet periods.
The framework integrates with AWS Application Auto Scaling to provide target tracking scaling policies. This allows you to define a target metric - such as average CPU utilization or the number of requests per minute - and the framework will automatically manage the number of Fargate tasks to keep that metric at your desired level.
To enable autoscaling for a service, add an autoscaling block to its task
configuration in your .yml configuration file.
tasks: my-service: # ... other task settings ... autoscaling: min_capacity: 1 max_capacity: 10 cpu: 60
The autoscaling block accepts the following keys:
min_capacity (Required)
The minimum number of tasks to keep running at all times. The service will never scale in below this number.
max_capacity (Required)
The maximum number of tasks that the service can scale out to. This acts as a safeguard to control costs.
cpu OR requests (Required, mutually exclusive)
You must specify exactly one scaling metric.
cpu: The target average CPU utilization percentage across all tasks in
the service. Valid values are between 1 and 100.requests: The target number of requests per minute for each task. This
is only valid for tasks of type http or https that are behind an
Application Load Balancer.scale_in_cooldown (Optional)
The amount of time, in seconds, to wait after a scale-in activity before another scale-in activity can start. This prevents the service from scaling in too aggressively.
Default: 300
scale_out_cooldown (Optional)
The amount of time, in seconds, to wait after a scale-out activity before another scale-out activity can start. This allows new tasks time to warm up and start accepting traffic before the service decides to scale out again.
Default: 60
policy_name (Managed by CApp::FargateStack)
This is a unique name for the scaling policy generated by the framework. It is written to your configuration file and used to detect drift between your configuration and the live environment in AWS. You should not modify this value.
This configuration will maintain at least 1 task, scale up to a maximum of 5 tasks, and will add or remove tasks to keep the average CPU utilization at or near 60%.
tasks:
my-cpu-intensive-worker:
type: daemon
image: my-worker:latest
autoscaling:
min_capacity: 1
max_capacity: 5
cpu: 60
This configuration will maintain at least 2 tasks, scale up to a maximum of 20 tasks, and will add or remove tasks to keep the number of requests per minute for each task at or near 1000. It also specifies custom cooldown periods.
tasks:
my-website:
type: https
image: my-website:latest
autoscaling:
min_capacity: 2
max_capacity: 20
requests: 1000
scale_in_cooldown: 600
scale_out_cooldown: 120
To configure predictive, time-based scaling, add a scheduled block
inside the main autoscaling configuration. This allows you to
define named time windows for scaling.
Example:
autoscaling:
...
scheduled:
business_hours:
start_time: 00:18
end_time: 00:02
min_capacity: 2/1
max_capacity: 3/2
Note: start_time and end_time are UTC
scheduled (Optional)
A hash where each key is a unique, descriptive name for the schedule
group (e.g., business_hours). Each group defines a time window and
the capacity changes for that window.
MON-FRI) or comma-separated values.2/1 or 2,1).in/out format as
min_capacity.The parser will generate two scheduled actions from this block: one to
apply the "in" capacity at the start_time and one to apply the
"out" capacity at the end_time.
This configuration creates a robust scaling strategy. The service will reactively scale based on CPU load at all times, but the capacity "guardrails" will be adjusted automatically for business hours.
tasks:
my-website:
type: https
image: my-website:latest
autoscaling:
# Default metric-based scaling policy
min_capacity: 1
max_capacity: 10
cpu: 75
# Scheduled scaling actions to adjust the guardrails
schedule:
business_hours:
start_time: "09:00"
end_time: "18:00"
days: MON-FRI
min_capacity: 2/1
max_capacity: 10/2
CApp::FargateStack treats your YAML configuration as the single source of
truth. On every plan or apply run, it will compare the autoscaling
configuration in your file with the live scaling policy in AWS.
If it detects any differences (e.g., someone manually changed the max capacity
in the AWS Console), it will report the drift and will not apply any changes.
To overwrite the live settings and enforce the configuration from your file,
you must re-run the apply command with the --force flag. This provides a
critical safety check against accidental configuration changes.
autoscaling keywordFor any service type (https, http, daemon, or task), you can enable
and configure autoscaling directly from the command line. This provides a
quick-start method to make your service elastic from the moment it's created.
The autoscaling: keyword accepts a metric and an optional target value:
Enable with a specific target value:
autoscaling:requests=500 autoscaling:cpu=60
This will enable autoscaling and set the target for either ALB requests per task or average CPU utilization.
Enable with default target value:
autoscaling:requests autoscaling:cpu
If you omit the target value, a sensible default will be used (e.g.,
500 for requests, 60 for CPU).
When the create-stack command sees the Cautoscaling: keyword, it
will generate a complete autoscaling block in your fargate-stack.yml
file. This block will be populated with safe defaults (min_capacity: 1,
max_capacity: 2), the specified metric, and all other necessary fields,
making it easy to review and customize later. See "AUTOSCALING" for
a full list of configuration options.
app-FargateStack without manually updating the
configurationcpuArchitecture is "X86_64" LINUX. This is
unlikely to change.When running a task you may see:
[2025/08/05 03:40:58] run-task: subnet-id: [subnet-7c160c37] is in a public subnet...consider running your jobs in a private subnet
This means the task is being scheduled in a subnet that has a 0.0.0.0/0 route to an Internet Gateway (a public subnet).
While not fatal, placing tasks in public subnets is discouraged unless you have a specific need.
Running tasks in public subnets can introduce risk and operational surprises:
Accidental exposure
If the task is assigned a public IP and the security group allows inbound access, it may be reachable from the internet.
Unintended dependency
Public-subnet egress typically relies on a public IP and the Internet Gateway. That can bypass intended egress controls, logging, or central inspection.
Narrow security margin
Safety depends entirely on security groups and NACLs. A small misconfiguration can expose services or data.
Use private subnets for most Fargate workloads. Private subnets do not route directly to the internet.
If the task needs outbound access (for example, to pull images from ECR or call external APIs), use one of:
For public-facing applications, the common pattern is: tasks in private subnets, fronted by a public Application Load Balancer in public subnets.
Use a public subnet only when the task itself must have a public IP and terminate client connections directly (uncommon). If you do:
To pull from ECR, the task needs a path to ECR API, ECR DKR, and S3:
ResourceInitializationError: unable to pull secrets or registry auth:
The task cannot pull registry auth from Amazon ECR: There is a
connection issue between the task and Amazon ECR. Check your task
network configuration. operation error ECR: GetAuthorizationToken,
exceeded maximum number of attempts, 3, https response error
StatusCode: 0, RequestID: , request send failed, Post
"https://api.ecr.us-east-1.amazonaws.com/": dial tcp 44.213.79.10:443:
i/o timeout
This error usually occurs when your task is launched in a subnet that does not have outbound access to the internet. Internet access - or a properly configured VPC endpoint - is required for Fargate to authenticate with ECR and pull your container image.
Even though the subnet may have a route to an Internet Gateway (i.e., it is technically a "public" subnet), if the task does not receive a public IP, it cannot use that route to reach external services like ECR or Secrets Manager.
App::FargateStack attempts to prevent this situation by analyzing
your VPC configuration during planning. It categorizes subnets as
private or public and evaluates whether they provide the necessary
network access to launch a Fargate task successfully. The framework
warns if you attempt to use a subnet that lacks internet or endpoint
access.
This is one of the most common and frustrating scenarios when working
with Fargate. You run start-service or run-task, the command
seems to succeed, but then the task quickly stops. The status
command shows the desired count is 1 but the running count is 0, and
the logs are empty.
This often happens due to a resource initialization error. The problem isn't with your container image itself, but with the infrastructure Fargate is trying to set up for it.
Common causes include:
These errors are opaque because they happen deep inside the AWS-managed environment. The high-level ECS API only reports a generic failure, and since it's not an API call error, it won't appear in CloudTrail.
stoppedReasonTo solve this, App-FargateStack provides an optional argument to
the list-tasks command. By default, this command only shows
RUNNING tasks. However, if you add the stopped argument, it will
show recently stopped tasks and, most importantly, the reason they
stopped.
The Command:
app-FargateStack list-tasks stopped
This will display a table of stopped tasks, including a Stopped
Reason column. This column often contains the detailed, multi-line
error message from the underlying AWS service that caused the failure,
giving you the exact information you need to debug the problem.
For example, if an EFS mount failed, the stoppedReason might
contain:
ResourceInitializationError: failed to invoke EFS utils
commands... mount.nfs4: mounting failed, reason given by server: No
such file or directory
This tells you immediately that the problem is with the EFS path, not a generic "task failed" message.
This is one of the most common points of confusion when working with ECS and Fargate.
You may have just built and pushed a new image to ECR using the same
tag (e.g. latest), but when you launch a task or deploy a service,
ECS appears to continue using the old image. Here's why.
run-task uses a fixed image digestWhen you run a task using:
app-FargateStack run-task my-task
ECS uses the exact task definition revision as registered. If the
image was specified using a tag like :latest, ECS resolves that tag
once -- at the time the task starts -- and stores the resolved digest
(e.g. sha256:...).
This means:
latest tag in ECR now points to a newer image.The only way to run a task with the new image is to register a new task definition that references the updated image. You can force a new task definition by registering the definition.
app-FargateStack register my-task
create-service and update-service use frozen images tooWhen you create or update a service, ECS also resolves any image tags to their current digest and stores that in the registered task definition.
This means that ECS services are also tied to the image that existed at the time of task definition registration.
If you push a new image to ECR using the same tag (e.g. :latest),
the service will not automatically use it. ECS does not re-resolve
the tag unless you explicitly tell it to.
--force-new-deployment re-pulls image tags (if not pinned by digest)If your task definition references the image by tag
(e.g. http-service:latest), and not by digest, then running:
app-FargateStack redeploy my-service
will cause ECS to:
This allows your service to pick up a newly pushed image without registering a new task definition, as long as the task definition used a tag (not a digest).
To see whether your task definition uses a tag or a digest, run:
aws ecs describe-task-definition --task-definition my-task:42
Look at the image field under containerDefinitions. It will either be:
image: http-service:latest # tag -- will be re-resolved by --force-new-deployment
image: http-service@sha256:... # digest -- frozen, cannot be re-resolved
:latest in production. Use immutable tags
(e.g. :v1.2.3) or digests.If you want to deploy a new image, the safest and most deterministic approach is to:
- Build and push the image using a new tag or digest
- Register a new task definition revision referencing that tag or digest
- Update your service to use the new task definition
Use --force-new-deployment only if your task definition uses a tag
and you want to re-resolve it without changing the task definition
itself.
cluster using a single configuration file?
Yes. The tasks: section of the configuration allows you to create
multiple tasks. A typical stack might consist of:
No. While the framework is written in Perl, you do not need to be a Perl developer or even have Perl installed on your system to use it.
The recommended way to use App::FargateStack is via its
containerized version, which is invoked using the
app-FargateStack-docker script. This version bundles Perl and all
its dependencies into a single Docker image. As long as you have
Docker installed, you have everything you need.
We chose Perl for its powerful text processing and system integration capabilities, which make it an excellent "glue" language for wrapping the AWS CLI (you can read more about this in the "BACKGROUND" section).
Ultimately, we believe the tool's ability to simplify Fargate deployments speaks for itself. We encourage you to give the Docker version a try - you might find you like it, no Perl knowledge required.
No, you should not. The configuration file is treated as the single source of truth.
On every plan or apply run, the framework regenerates the task
definition file (taskdef-{task-name}.json) based on the settings in your
YAML configuration. Any manual edits you make to the JSON file will be
overwritten the next time you run the tool.
The documentation explicitly warns: "You should not manually modify the generated file... as doing so may cause App::FargateStack to lose track of your task's configuration."[cite: 955]. If you need to change your task's configuration, you must do so in the YAML configuration file.
No. The framework operates on a "safe by default" principle and will
not modify or delete any AWS resources that are not explicitly defined
in your fargate-stack.yml file.
For example, App::FargateStack will not delete:
It is designed to manage only the resources that are within its defined scope.
This project is driven by the goal of making AWS Fargate simple and accessible. Community feedback and contributions are highly encouraged and appreciated. Here are the best ways to get involved:
Try it and provide feedback
The primary goal is for this framework to be easy to use. The benchmark is to get a new user up and running in under 10 minutes. If that's not your experience, please open an issue on GitHub and let us know where you got stuck. Real-world feedback is the most valuable contribution you can provide.
Improve the documentation
Writing good software is hard; writing good documentation is even harder. If you find a section that is confusing, unclear, or incorrect, please let us know by opening an issue or submitting a pull request with your suggested changes.
Suggest new features
If there is a feature you believe is missing, please suggest it! Keeping in mind the project's philosophy of simplicity, we welcome ideas for enhancements that make Fargate easier to use. Before you reach for a complex solution like EKS, consider if there's a simpler path with Fargate. If this tool can help bridge that gap, we want to hear about it.
Contribute code
Pull requests are always welcome. Whether it's a bug fix, a new feature, or a refactoring to improve the code, your contributions are valued.
This framework is the logical evolution of many projects that required provisioning resources for ECS. Those early projects typically avoided heavy IaaC tools like Terraform in favor of a lighter approach using bash scripts and the AWS CLI.
Naturally, as a Perl developer, I eventually concluded that Perl is a
superior "glue" language than bash. That led me to write App::AWS, a
lightweight Perl wrapper around the AWS CLI that eschewed the heavier
PAWS module. This wrapper proved to be a convenient and powerful way
to interact with the AWS CLI, combining Perl's magnificent data
structures with the CLI's JMESPath query capabilities.
What started as a simpler way to write provisioning scripts has now
evolved into the framework you see today: App::FargateStack.
While App::FargateStack is designed to be a high-level abstraction,
this section provides a brief overview of its internal mechanics for
users who wish to understand its behavior more deeply for debugging or
contribution purposes.
App::FargateStack does not use the AWS SDKs directly. Instead, it acts as an
intelligent wrapper around the AWS CLI. Every action that modifies or queries
your AWS environment is performed by shelling out and executing a corresponding
aws command.
This design choice makes the tool's behavior highly transparent. If
you ever need to see the exact AWS API calls being made, you can run
any command with the --log-level debug or trace flag, which will
print the full AWS CLI commands being executed.
As mentioned in the "Configuration as State" section, the YAML file is more
than just an input. The plan command enriches this file by populating it
with the ARNs, IDs, and names of discovered or newly provisioned resources.
On subsequent runs, the framework uses this enriched file as a cache. By reading the ARNs directly from the file, it avoids making unnecessary "describe" API calls to AWS, which significantly speeds up execution. This is why the YAML file should be considered a managed state file and checked into version control alongside your application code.
Before creating certain AWS resources, the framework first generates local
JSON files that represent the resource's configuration. The most common
example is the taskdef-{task-name}.json file created before registering a
new task definition.
These files are intermediate artifacts managed entirely by the tool. As noted in the "FAQ", they should not be edited manually, as they are regenerated on each run from the "single source of truth" - your YAML configuration file.
Repository
Docker Hub
Website
CPAN
AWS Links
IPC::Run, App::Command, App::AWS, CLI::Simple
Rob Lauer - rclauer@gmail.com
This script is released under the same terms as Perl itself.