App-FargateStack

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

  * [USING EXISTING BUCKETS](#using-existing-buckets)
* [HTTP SERVICES](#http-services)
  * [Overview](#overview)
  * [Key Assumptions When Creating HTTP Services](#key-assumptions-when-creating-http-services)
  * [Architecture](#architecture)
  * [Behavior by Task Type](#behavior-by-task-type)
  * [ACM Certificate Management](#acm-certificate-management)
  * [Port and Listener Rules](#port-and-listener-rules)
  * [Example Minimal Configuration](#example-minimal-configuration)
  * [Application Load Balancer](#application-load-balancer)
    * [Why Does the Framework Force the Use of a Load Balancer?](#why-does-the-framework-force-the-use-of-a-load-balancer)
  * [AWS WAF Support](#aws-waf-support)
    * [Enabling WAF Protection](#enabling-waf-protection)
    * [Configuring Managed Rules](#configuring-managed-rules)
      * [Rule Set Keywords](#rule-set-keywords)
      * [Rule Bundles](#rule-bundles)
    * [The Bootstrap Process (First Run)](#the-bootstrap-process-first-run)
    * [Ongoing Management (Subsequent Runs)](#ongoing-management-subsequent-runs)
    * [Conflict and Drift Management](#conflict-and-drift-management)
    * [Estimated Cost](#estimated-cost)
  * [Roadmap for HTTP Services](#roadmap-for-http-services)

README.md  view on Meta::CPAN

    # Dry-run and analyze the configuration
    app-FargateStack plan -c my-stack.yml

    # Provision the full stack
    app-FargateStack apply -c my-stack.yml

[Back to Table of Contents](#table-of-contents)

# DESCRIPTION

**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

README.md  view on Meta::CPAN

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](https://github.com/rlauer6/App-FargateStack/issues).

## Caveats

- The documentation may be incomplete or inaccurate.
- Features may change, and new ones will be added. See the
["ROADMAP"](#roadmap) for details.
- Deploying resources using this framework may result in AWS charges.
- This software is provided "as is", without warranty of any kind.
Use at your own risk.

## Features

- Minimal configuration: launch a Fargate service with just a task name
and container image
- Supports multiple task types: HTTP, HTTPS, daemon, cron (scheduled)
- Automatic resource provisioning: IAM roles, log groups, target groups,
listeners, etc.

README.md  view on Meta::CPAN

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.
- (7) Use `--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.
- (8) To speed up processing and avoid unnecessary API calls the
framework considers the configuration file the source of truth and a
reliable representation of the state of the stack. If you want to
re-sync the configuration file set `--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`.
- (9) `--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`.
- (10) Set `--route53-profile` to the profile that has
permissions to manage your hosted zones. By default the script will
use the default profile.
- (11) Deleting a task, daemon, or http service will delete all of
the resources associated with that task.

README.md  view on Meta::CPAN

        In this case, your scaling policy `max_capacity` value must be at least
        4.

[Back to Table of Contents](#table-of-contents)

# OVERVIEW

_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:

- ...build internal or external facing HTTP services that:
    - ...automatically provision certificates for external facing web applications
    - ...use an existing or create a new internal or external facing application load balancer (ALB).
    - ...automatically create an alias record in Route 53 for your domain
    - ...create a listener rule to redirect port 80 requests to 443 
- ...create queues and buckets to support your application
- ...use a dryrun mode to report the resources that will be built
before building them
- ...run `app-FargateStack` multiple times (idempotency)

README.md  view on Meta::CPAN

- support for scheduled and metric based [autoscaling](#autoscaling)

## Minimal Configuration

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:

- Create a Docker image that implements our worker, web app or API
- Create a minimal configuration file that describes our application
- Execute the framework's script and create the necessary AWS infrastructure
- Launch the http server, daemon, scheduled job, or adhoc worker

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"](#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:

- a cluster named `my-stack-cluster`
- a security group for the cluster
- an IAM role for the the cluster
- an IAM  policy that has permissions enabling your worker
- an ECS task definition that describes your task
- a CloudWatch log group
- an EventBridge target event
- an IAM role for EventBridge
- an IAM policy for EventBridge

README.md  view on Meta::CPAN

- An ALB if one is not detected

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 Changing Resources

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.

    my-worker:
      image: my-worker:latest
      command: /usr/local/bin/my-worker.pl
      type: task
      schedule: cron(00 15 * * * *)   
      bucket:
        name: my-worker-bucket

README.md  view on Meta::CPAN

        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.

## Configuration as State

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

[Back to Table of Contents](#table-of-contents)

# CLI OPTION DEFAULTS

README.md  view on Meta::CPAN


Only explicitly provided CLI options are tracked. Values derived from
environment variables or configuration files are not saved.

This feature is enabled by default.

[Back to Table of Contents](#table-of-contents)

# COMMAND LIST

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.

## Configuration File Naming

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

README.md  view on Meta::CPAN


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

    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

README.md  view on Meta::CPAN


- This command generates config; it does not deploy. Run your normal "plan/apply"
flow after reviewing the YAML.
- For HTTP/HTTPS, `domain:` is required at creation time in this shorthand.
- Always quote `schedule:...` to avoid shell interpretation of parentheses.

### deploy-service

    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-FargateTask deploy-service service-name

If you want to start multiple tasks for the service, you can include a
count argument:

README.md  view on Meta::CPAN

    CPU
    Start Time
    Elapsed Time
    Stopped Reason

### list-zones

    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

    logs start-time end-time

README.md  view on Meta::CPAN

configuration file.

### tasks

Displays a table that summarizes your stack resources.

### update-policy

    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.

README.md  view on Meta::CPAN


For example, given the following configuration:

    app:
      name: my-stack
    ...
    tasks:
      apache:
        type: https

The framework will:

- ...create a log group named /ecs/my-stack
- ...configure the apache task to write log streams with a prefix
like my-stack/apache/\*

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:

README.md  view on Meta::CPAN


- You can change the retention period by updating the
configuration file and re-running `apply`.
- To retain logs indefinitely, remove the `retention_days`
entry in your configuration file.

[Back to Table of Contents](#table-of-contents)

# IAM PERMISSIONS

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 policy is updated when new resources are

README.md  view on Meta::CPAN

`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.

## Task Execution Role vs. Task Role

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.

[Back to Table of Contents](#table-of-contents)

# SECURITY GROUPS

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

README.md  view on Meta::CPAN

      mount_point: /mnt/my-stack
      path: /
      readonly:

Acceptable values for `readonly` are "true" and "false".

## Field Descriptions

- 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.

README.md  view on Meta::CPAN

    Optional. Set to `true` to mount the EFS as read-only. Defaults to
    `false`.

## Additional Notes

- The ECS role's policy for your task is automatically modified
to allow read/write EFS access. Set `readonly:` in your task's
`efs:` section to "true" if only want read support.
- Your EFS security group must allow access from private subnets
where the Fargate tasks are placed.
- No changes are made to the EFS security group; the framework
assumes access is already configured
- Only one EFS volume is currently supported per task configuration.
- EFS volumes are task-scoped and reused only where explicitly configured.
- The framework does not automatically provision an EFS
filesystem for you. The framework does however validate that the
filesystem exists in the current account and region.

[Back to Table of Contents](#table-of-contents)

# CONFIGURATION

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`.

## GETTING STARTED

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.

README.md  view on Meta::CPAN

      my-stack-daemon:
        image: my-stack-daemon:latest
        type: daemon

This file contains the three key pieces of information you provided:
the application name, the task name, and the image to use.

### Step 2: Plan the Deployment (Dry Run)

Next, run the `plan` command. This is a crucial step that acts as a
dry run. The framework will:

- Read your minimal configuration file.
- Intelligently discover resources in your AWS account (like your VPC and subnets).
- Determine what new resources need to be created (like IAM roles, a security group, an ECS cluster and a CloudWatch log group).
- Report a full plan of action without making any actual changes.
- Update your configuration file with the discovered values and
sensible defaults.

    app-FargateStack plan

README.md  view on Meta::CPAN

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

## VPC AND SUBNET DISCOVERY

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:

- It is attached to an Internet Gateway (IGW)
- It has at least one available NAT Gateway

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.

## SUBNET SELECTION

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.

### Task placement and Availability Zones

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.

README.md  view on Meta::CPAN

      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

## FULL SCHEMA OVERVIEW

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:

README.md  view on Meta::CPAN

         target_group_arn:
         target_group_name:
         task_definition_arn:
         type:            # required (daemon, task, http, https)
     vpc_id:

[Back to Table of Contents](#table-of-contents)

# TASK SIZE

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.

[Back to Table of Contents](#table-of-contents)

# ENVIRONMENT VARIABLES

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.

## BASIC USAGE

    task:
      apache:

README.md  view on Meta::CPAN

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.

[Back to Table of Contents](#table-of-contents)

# SQS QUEUES

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._

## BASIC CONFIGURATION

README.md  view on Meta::CPAN

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`).

## DEFAULT QUEUE ATTRIBUTES

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

README.md  view on Meta::CPAN

## IAM POLICY UPDATES

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).

[Back to Table of Contents](#table-of-contents)

# SCHEDULED JOBS

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 job is defined like any other task, using `type: task`, and
adding a `schedule:` key in AWS EventBridge cron format.

## SCHEDULING A JOB

To schedule a job, add a `schedule:` key to your task definition. The
value must be a valid AWS cron expression, such as:

README.md  view on Meta::CPAN

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.

## RUNNING AN ADHOC JOB

You can run a scheduled (or unscheduled) task manually at any time using:

README.md  view on Meta::CPAN

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.

[Back to Table of Contents](#table-of-contents)

# S3 BUCKETS

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._

README.md  view on Meta::CPAN

The `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.

## IAM-BASED ENFORCEMENT

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.

## USING EXISTING BUCKETS

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:

- The IAM role created by the framework may permit access to a path
- But a bucket policy with an explicit `Deny` will override that and block access
- This restriction will only be discovered at runtime when your task attempts access

To avoid surprises, ensure that any bucket policy on an external bucket
permits access from the IAM role you're configuring.

[Back to Table of Contents](#table-of-contents)

# HTTP SERVICES

README.md  view on Meta::CPAN

_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.

## ACM Certificate Management

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.

## Port and Listener Rules

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".

README.md  view on Meta::CPAN

    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:

- ...determining your VPC's ID
- ...identifying the private subnet IDs
- ...determining if there is and existing load balancer with the
correct scheme
- ...finding your load balancer's security group (if an ALB exists)

README.md  view on Meta::CPAN

discovered to be missing from your environment.

## Application Load Balancer

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.

### Why Does the Framework Force the Use of a Load Balancer?

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.

## AWS WAF Support

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.

### Enabling WAF Protection

To enable WAF, simply add a `waf` block with `enabled: true` to your
`alb` configuration:

    alb:
      # ... existing alb configuration ...
      waf:
        enabled: true

### Configuring Managed Rules

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.

#### Rule Set Keywords

- **base**: A strong baseline including `AWSManagedRulesCommonRuleSet`, `AWSManagedRulesAmazonIpReputationList`, and `AWSManagedRulesKnownBadInputsRuleSet`.
- **admin**: Protects exposed administrative pages (`AWSManagedRulesAdminProtectionRuleSet`).
- **sql**: Protects against SQL injection attacks (`AWSManagedRulesSQLiRuleSet`).
- **linux**: Includes rules for Linux and Unix-like environments.

README.md  view on Meta::CPAN

#### Rule Bundles

- **default**: Includes `base` and `sql`. This is the recommended starting point for most applications.
- **linux-app**: Includes `default` and `linux`.
- **wordpress-app**: Includes `default`, `linux`, and `wordpress`.
- **windows-app**: Includes `default` and `windows`.
- **all**: Includes all standard, non-premium rule sets. **Warning:** This will likely exceed the default WCU quota and may incur additional costs.

### The Bootstrap Process (First Run)

On the first `apply` run with WAF enabled, the framework will perform
a one-time bootstrap:

1. It generates a default `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.
2. It calls `aws wafv2 create-web-acl` to create a new Web ACL.
3. It calls `aws wafv2 associate-web-acl` to link the new Web ACL to
your Application Load Balancer.
4. It updates your configuration file with the state of the new
WAF resources, including its Name, ID, ARN, LockToken, and a checksum

README.md  view on Meta::CPAN

- If the checksum has not changed, it will skip the update to avoid
unnecessary API calls.

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.

### Conflict and Drift Management

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.

### Estimated Cost

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:

- **$5.00 / month** for the Web ACL itself.
- **$4.00 / month** for the four AWS Managed Rule Groups included
in the `default` bundle (3 in 'base', 1 in 'sql').

README.md  view on Meta::CPAN


# AUTOSCALING

## Overview

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.

## Enabling Autoscaling

To enable autoscaling for a service, add an `autoscaling` block to its task
configuration in your .yml configuration file.

tasks:
  my-service:

README.md  view on Meta::CPAN

- **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.

## Example: Scaling on CPU Utilization

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%.

README.md  view on Meta::CPAN


- Stacks may contain multiple daemon services, but only one task
may be exposed as an HTTP/HTTPS service via an ALB.
- Limited configuration options for some resources such as
advanced load balancer listener rules, custom CloudWatch metrics, or
task health check tuning.
- Some out of band infrastructure changes may break the ability
to re-run `app-FargateStack` without manually updating the
configuration
- Support for only 1 EFS filesystem per task
- This framework assumes that the
[operatingSystemFamily](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters_ec2.html#runtime-platform_ec2)
is "LINUX" and the `cpuArchitecture` is "X86\_64" LINUX. This is
unlikely to change.

[Back to Table of Contents](#table-of-contents)

# TROUBLESHOOTING

## Warning: task placed in a public subnet

README.md  view on Meta::CPAN

and the subnet has a route to it.
- Alternatively, configure VPC endpoints for ECR, Secrets
Manager, and related services to avoid needing internet access
altogether.

### Note on Subnet Selection

`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.

## My task failed to start and the reason is unclear

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.

lib/App/FargateStack/Checker.pm  view on Meta::CPAN

  }

  return row_ok( 'Secrets Manager', 'Endpoint present' );
}
########################################################################
sub check_passrole {
########################################################################
  my ($opt) = @_;
  my $role_names = $opt->get_role_names;

  # role_names: arrayref of the exact role names your framework will create,
  # e.g., [ 'FargateStack/my-svc/TaskExecutionRole', 'FargateStack/my-svc/TaskRole',
  #         'FargateStack/my-svc/EventsInvokeRole' ]

  my $sts = new_client( 'App::STS', $opt );
  my $iam = new_client( 'App::IAM', $opt );

  my $id         = $sts->get_caller_identity();
  my $acct       = $id->{Account} || q{};
  my $caller_arn = $id->{Arn}     || q{};

lib/App/FargateStack/Constants.pm  view on Meta::CPAN

########################################################################
Readonly::Hash our %HELP_SUBJECTS => (
  'tbd' => [
    'TBD' => <<'END_OF_TEXT'
Our current TODO list. Add your request at
https://github.com/rlauer6/App-Fargate
END_OF_TEXT
  ],
  'overview' => [
    'OVERVIEW' => <<'END_OF_TEXT'
An overview of the App::FargateStack framework.
END_OF_TEXT
  ],
  'cloudwatch logs' => [
    'CLOUDWATCH LOGS' => <<'END_OF_TEXT'
Information on Cloudwatch log groups and how to view logs from your tasks.
END_OF_TEXT
  ],
  'command list' => [
    'COMMAND LIST' => <<'END_OF_TEXT'
A detailed description of available commands.
END_OF_TEXT
  ],
  'daemon services' => [
    'DAEMON SERVICES' => <<'END_OF_TEXT'
Information on how to create long running daemon services using this
framework.
END_OF_TEXT
  ],
  'scheduled jobs' => [
    'SCHEDULED JOBS' => <<'END_OF_TEXT'
A description of using the framework to create scheduled and one-shot workloads.
END_OF_TEXT
  ],
  'task size' => [
    'TASK SIZE' => <<'END_OF_TEXT'
Using the "size:" key to set the task's memory and cpu parameters.
END_OF_TEXT
  ],
  'http services' => [
    'HTTP SERVICES' => <<'END_OF_TEXT'
A description of how the framework can provision a fully functional
web application using Fargate.
END_OF_TEXT
  ],

  'log groups' => [
    'CLOUDWATCH LOG GROUPS' => <<'END_OF_TEXT'
Information on how log groups are provisioned and configure.
END_OF_TEXT
  ],
  'iam permissions' => [
    'IAM PERMISSIONS' => <<'END_OF_TEXT'
A discussion of how the framework creates IAM roles and policies for
the resources used in your tasks.
END_OF_TEXT
  ],
  'environment variables' => [
    'ENVIRONMENT VARIABLES' => <<'END_OF_TEXT'
How to injecting environment variables into your container. Also
include information on using secrets fromSecretsManager in your
environment.
END_OF_TEXT
  ],

lib/App/FargateStack/Constants.pm  view on Meta::CPAN

END_OF_TEXT
  ],
  'filesystem support' => 'efs support',
  'buckets'            => [
    'S3 BUCKETS' => <<'END_OF_TEXT'
Creating and configuring S3 buckets.
END_OF_TEXT
  ],
  'networking' => [
    'NETWORKING' => <<'END_OF_TEXT'
Explanation of how the framework recognizes and uses your networking
resources.
END_OF_TEXT
  ],
  'roadmap' => [
    'ROADMAP' => <<'END_OF_TEXT'
A peek at what's next for App::FargateStack.
END_OF_TEXT
  ],
  'cli option defaults' => [
    'CLI OPTION DEFAULTS' => <<'END_OF_TEXT'

lib/App/FargateStack/Constants.pm  view on Meta::CPAN

    'LIMITATIONS' => <<'END_OF_TEXT'
END_OF_TEXT
  ],
  'troubleshooting' => [
    'TROUBLESHOOTING' => <<'END_OF_TEXT'
Hints and tips for troubleshooting.
END_OF_TEXT
  ],
  'security groups' => [
    'SECURITY GROUPS' => <<'END_OF_TEXT'
How the framework provisions and configures security groups.
END_OF_TEXT
  ],
);

Readonly::Scalar our $LOG4PERL_CONF => <<'END_OF_CONF';
log4perl.logger = INFO, Screen
log4perl.appender.Screen = Log::Log4perl::Appender::ScreenColoredLevels
log4perl.appender.Screen.stderr = 1
log4perl.appender.Screen.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.Screen.layout.ConversionPattern = [%d] %m%n

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

=head1 SYNOPSIS

 # Dry-run and analyze the configuration
 app-FargateStack plan -c my-stack.yml

 # Provision the full stack
 app-FargateStack apply -c my-stack.yml

=head1 DESCRIPTION

B<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, B<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

lib/App/FargateStack/Pod.pm  view on Meta::CPAN


=head2 Caveats

=over 4

=item * The documentation may be incomplete or inaccurate.

=item * Features may change, and new ones will be added. See the
L</ROADMAP> for details.

=item * Deploying resources using this framework may result in AWS charges.

=item * This software is provided "as is", without warranty of any kind.
Use at your own risk.

=back

=head2 Features

=over 4

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

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.

=item (7) Use C<--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.

=item (8) To speed up processing and avoid unnecessary API calls the
framework considers the configuration file the source of truth and a
reliable representation of the state of the stack. If you want to
re-sync the configuration file set C<--no-cache> and run C<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 C<plan> or C<apply>.

=item (9) C<--no-update> is not permitted with C<apply>. If you need a
dry plan without applying or updating the config, use C<--dryrun> (and
optionally C<--no-update>) with C<plan>.

=item (10) Set C<--route53-profile> to the profile that has
permissions to manage your hosted zones. By default the script will
use the default profile.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN


=back

=back

=head1 OVERVIEW

I<NOTE: This is a brief introduction to C<App::FargateStack>. To see a 
list of topics providing more detail use the C<help help> command.>

The C<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 C<app-FargateStack> script and
launch your application.

Using this framework you can:

=over 4

=item * ...build internal or external facing HTTP services that:

=over 8

=item * ...automatically provision certificates for external facing web applications

=item * ...use an existing or create a new internal or external facing application load balancer (ALB).

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

=back

=head2 Minimal Configuration

Getting a Fargate task up and running requires that you provision and
configure multiple AWS resources. Stitching it together using
B<Terraform> or B<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:

=over 4

=item Create a Docker image that implements our worker, web app or API

=item Create a minimal configuration file that describes our application

=item Execute the framework's script and create the necessary AWS infrastructure

=item Launch the http server, daemon, scheduled job, or adhoc worker

=back

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 * * * *)

I<TIP: You can use the L</create-stack> command to create minimal
configuration files for various Fargate application scenarios.>

Using this minimal configuration and running C<app-FargateStack> like this:

 app-FargateStack plan

...the framework would create the following resources in your VPC:

=over 8

=item * a cluster named C<my-stack-cluster>

=item * a security group for the cluster

=item * an IAM role for the the cluster

=item * an IAM  policy that has permissions enabling your worker

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

=back

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.

=head2 Adding or Changing Resources

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 C<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.

 my-worker:
   image: my-worker:latest
   command: /usr/local/bin/my-worker.pl
   type: task
   schedule: cron(00 15 * * * *)   
   bucket:
     name: my-worker-bucket

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

     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 C<app-Fargate help configuration> for more information about
resources and options.

=head2 Configuration as State

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 I<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

=head1 CLI OPTION DEFAULTS

When enabled, C<App::FargateStack> automatically remembers the most recently
used values for several CLI options between runs. This feature helps streamline

lib/App/FargateStack/Pod.pm  view on Meta::CPAN


=head2 Notes

Only explicitly provided CLI options are tracked. Values derived from
environment variables or configuration files are not saved.

This feature is enabled by default.

=head1 COMMAND LIST

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.

=head2 Configuration File Naming

Your configuration file can be named anything, but by convention your
configuration file should have a F<.yml> extension. If you don't
provide a configuration filename the default configuration file

lib/App/FargateStack/Pod.pm  view on Meta::CPAN


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.

=head3 create-stack

 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 B<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

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

=item *

Always quote C<schedule:...> to avoid shell interpretation of parentheses.

=back

=head3 deploy-service

 deploy-service service-name

When you provision an HTTP, HTTPS, or daemon service, the framework
sets up all the necessary infrastructure components -- but it B<does not>
automatically create and start the ECS service.

Use this command to start the service:

  app-FargateTask deploy-service service-name

If you want to start multiple tasks for the service, you can include a
count argument:

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

 CPU
 Start Time
 Elapsed Time
 Stopped Reason

=head3 list-zones

 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 C<zone_id:> key is missing from your configuration when
you have an HTTP or HTTPS task defined.

Example:

 app-FargateStack list-zones --profile prod

=head3 logs

 logs start-time end-time

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

configuration file.

=head3 tasks

Displays a table that summarizes your stack resources.

=head3 update-policy

 update-policy

Forces the framework to re-evaluate resources and align the
policy. Will not apply changes in C<--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 C<update-policy> identifies a need to update your role policy, you
can view the changes before they are applied by running the C<plan>
command at the C<trace> log level.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN


For example, given the following configuration:

 app:
   name: my-stack
 ...
 tasks:
   apache:
     type: https

The framework will:

=over 4

=item * ...create a log group named /ecs/my-stack

=item * ...configure the apache task to write log streams with a prefix
like my-stack/apache/*

=back

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

=item * You can change the retention period by updating the
configuration file and re-running C<apply>.

=item * To retain logs indefinitely, remove the C<retention_days>
entry in your configuration file.

=back

=head1 IAM PERMISSIONS

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 policy is updated when new resources are

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

C<role:> key in the configuration. The task role is found under the
C<task_role:> key. Role names and role policy names are automatically
fabricated for you from the name you specified under the C<app:> key.

=head2 Task Execution Role vs. Task Role

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.

=head1 SECURITY GROUPS

A security group is automatically provisioned for your Fargate
cluster.  If you define a task of type C<http> or C<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.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

   readonly:

Acceptable values for C<readonly> are "true" and "false".

=head2 Field Descriptions

=over 4

=item 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.

=item mount_point:

The container path to which the EFS volume will be mounted.

=item path:

The path on the EFS filesystem to map to your container's mount point.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN


=over 4

=item * The ECS role's policy for your task is automatically modified
to allow read/write EFS access. Set C<readonly:> in your task's
C<efs:> section to "true" if only want read support.

=item * Your EFS security group must allow access from private subnets
where the Fargate tasks are placed.

=item * No changes are made to the EFS security group; the framework
assumes access is already configured

=item * Only one EFS volume is currently supported per task configuration.

=item * EFS volumes are task-scoped and reused only where explicitly configured.

=item * The framework does not automatically provision an EFS
filesystem for you. The framework does however validate that the
filesystem exists in the current account and region.

=back

=head1 CONFIGURATION

The C<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 C<plan> or
C<apply>.

=head2 GETTING STARTED

The fastest way to get up and running with C<App::FargateStack> is to
use the C<create-stack> command to generate a configuration file,
inspect the deployment plan, and then apply it.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

    my-stack-daemon:
      image: my-stack-daemon:latest
      type: daemon

This file contains the three key pieces of information you provided:
the application name, the task name, and the image to use.

=head3 Step 2: Plan the Deployment (Dry Run)

Next, run the C<plan> command. This is a crucial step that acts as a
dry run. The framework will:

=over 4

=item * Read your minimal configuration file.

=item * Intelligently discover resources in your AWS account (like your VPC and subnets).

=item * Determine what new resources need to be created (like IAM roles, a security group, an ECS cluster and a CloudWatch log group).

=item * Report a full plan of action without making any actual changes.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

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

=head2 VPC AND SUBNET DISCOVERY

If you do not specify a C<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:

=over 4

=item * It is attached to an Internet Gateway (IGW)

=item * It has at least one available NAT Gateway

=back

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 C<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.

=head2 SUBNET SELECTION

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.

=head3 Task placement and Availability Zones

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:

=over 4

=item * 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.

=item * 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.

=item * 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.

=back

Notes on internet access and ALBs:

=over 4

=item * Internet-facing ALB

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

    my-task:
      image: my-image
      type: daemon | task | http | https

For task types C<http> or C<https>, you must also specify a domain name:

  domain: example.com

=head2 FULL SCHEMA OVERVIEW

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:

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

      name:
      size:
      target_group_arn:
      target_group_name:
      task_definition_arn:
      type:            # required (daemon, task, http, https)
  vpc_id:

=head1 TASK SIZE

To simplify task configuration, the framework supports a shorthand key called
C<size> that maps to common CPU and memory combinations supported by Fargate.

If specified, the C<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 C<size> is provided, the framework will automatically populate the
corresponding C<cpu> and C<memory> values in the task definition. If you
manually specify C<cpu> or C<memory> alongside C<size>, those manual values
will take precedence and override the defaults from the profile.

B<Important:> If you change the C<size> after an initial deployment, you should
remove any manually defined C<cpu> and C<memory> keys in your configuration.
This ensures that the framework can correctly apply the new profile values
without conflict.

If neither C<size>, C<cpu>, nor C<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.

=head1 ENVIRONMENT VARIABLES

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 C<environment:> key within the task
configuration.

=head2 BASIC USAGE

  task:
    apache:

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

=head2 BEST PRACTICES

Avoid placing secrets in the C<environment:> block. That block is for non-sensitive
configuration values and exposes data in plaintext.

Use clear, descriptive environment variable names (e.g., C<DB_PASSWORD>, C<API_KEY>)
and organize your Secrets Manager paths consistently with your stack naming.

=head1 SQS QUEUES

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).

I<Only one queue and one optional DLQ may be configured per stack.>

=head2 BASIC CONFIGURATION

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

will be created automatically. You can optionally override its name
and attributes using the top-level C<dlq> key:

  queue:
    name: fu-man-q
    max_receive_count: 5

  dlq:
    name: custom-dlq-name

If you do not specify a C<dlq.name>, the framework defaults to appending C<-dlq> to
the main queue name (e.g., C<fu-man-q-dlq>).

=head2 DEFAULT QUEUE ATTRIBUTES

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

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

If no overrides are provided, DLQ attributes default to AWS attribute defaults.

=head2 IAM POLICY UPDATES

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).

=head1 SCHEDULED JOBS

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 job is defined like any other task, using C<type: task>, and
adding a C<schedule:> key in AWS EventBridge cron format.

=head2 SCHEDULING A JOB

To schedule a job, add a C<schedule:> key to your task definition. The
value must be a valid AWS cron expression, such as:

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

Example:

  tasks:
    daily-report:
      type: task
      image: report-runner:latest
      schedule: cron(0 2 * * ? *)

I<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.

=head2 RUNNING AN ADHOC JOB

You can run a scheduled (or unscheduled) task manually at any time using:

lib/App/FargateStack/Pod.pm  view on Meta::CPAN


A task of type C<daemon> is launched as a long-running ECS service
and benefits from restart policies and availability guarantees.

A task of type C<task> is run using C<run-task> and may run once,
forever, or periodically - but it will not be automatically restarted
if it fails.

=head1 S3 BUCKETS

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.

I<Note: Full access includes s3:GetObject, s3:PutObject, s3:DeleteObject, and
s3:ListBucket.  Readonly access is limited to s3:GetObject and
s3:ListBucket.>

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

The C<paths:> values are interpreted as S3 key prefixes and inserted
directly into the role policy.

If you specify C<readonly: true> but omit C<paths:>, read-only access will
apply to the entire bucket. If you omit both keys, full read/write access
is granted.

=head2 IAM-BASED ENFORCEMENT

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.

=head2 USING EXISTING BUCKETS

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:

=over 4

=item * The IAM role created by the framework may permit access to a path

=item * But a bucket policy with an explicit C<Deny> will override that and block access

=item * This restriction will only be discovered at runtime when your task attempts access

=back

To avoid surprises, ensure that any bucket policy on an external bucket
permits access from the IAM role you're configuring.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

I<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 B<private> and B<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
C<--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.

=head2 ACM Certificate Management

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.

=head2 Port and Listener Rules

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 C<redirect_80:> in
the C<alb:> section to "false".

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

 domain: http-test.example.com
 task:
   apache:
     type: http
     image: http-test:latest

Based on this minimal configuration C<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:

=over 4

=item  ...determining your VPC's ID

=item  ...identifying the private subnet IDs

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

discovered to be missing from your environment.

=head2 Application Load Balancer

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
I<internally> or I<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 C<alb:> section of the
configuration or pass C<--create-alb> when you run our plan and apply.

If no ALB is found or you passed the C<--create-alb> option, a new ALB
is provisioned. When creating a new ALB, C<app-FargateStack> will also
create the necessary listeners and listener rules for the ports you
have configured.

=head3 Why Does the Framework Force the Use of a Load Balancer?

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.

=head2 AWS WAF Support

For external-facing HTTPS services, C<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.

=head3 Enabling WAF Protection

To enable WAF, simply add a C<waf> block with C<enabled: true> to your
C<alb> configuration:

  alb:
    # ... existing alb configuration ...
    waf:
      enabled: true

=head3 Configuring Managed Rules

To simplify configuration, C<App::FargateStack> uses a keyword-based
system for enabling AWS Managed Rule Groups. You can specify a list of
keywords under the C<managed_rules> key in your C<waf> configuration.

If the C<managed_rules> key is omitted, the framework will apply the
C<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 C<->) to remove rule sets from a
bundle.

=head4 Rule Set Keywords

=over 4

=item * B<base>: A strong baseline including C<AWSManagedRulesCommonRuleSet>, C<AWSManagedRulesAmazonIpReputationList>, and C<AWSManagedRulesKnownBadInputsRuleSet>.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

=item * B<wordpress-app>: Includes C<default>, C<linux>, and C<wordpress>.

=item * B<windows-app>: Includes C<default> and C<windows>.

=item * B<all>: Includes all standard, non-premium rule sets. B<Warning:> This will likely exceed the default WCU quota and may incur additional costs.

=back

=head3 The Bootstrap Process (First Run)

On the first C<apply> run with WAF enabled, the framework will perform
a one-time bootstrap:

=over 4

=item 1.

It generates a default F<web-acl.json> file in your project
directory. This file contains the complete definition of your Web ACL,
including the rules generated from your C<managed_rules> keywords.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN


=back

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.

=head3 Conflict and Drift Management

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 I<and> you have also modified your local F<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.

=head3 Estimated Cost

The default WAF configuration is designed to provide a strong security
baseline while remaining cost-effective. When you enable WAF without
specifying any C<managed_rules>, the framework applies the C<default>
bundle, which includes the C<base> and C<sql> rule sets.

The approximate monthly cost for this default configuration is
B<~$9.00 per month>, plus per-request charges.

The cost is broken down as follows:

=over 4

=item * B<$5.00 / month> for the Web ACL itself.

lib/App/FargateStack/Pod.pm  view on Meta::CPAN


=head1 AUTOSCALING

=head2 Overview

For services that experience variable load, such as HTTP applications or
background job processors, C<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.

=head2 Enabling Autoscaling

To enable autoscaling for a service, add an C<autoscaling> block to its task
configuration in your .yml configuration file.

tasks:
  my-service:

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

=item * B<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: C<60>

=item * B<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.

=back

=head2 Example: Scaling on CPU Utilization

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

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

=item * Limited configuration options for some resources such as
advanced load balancer listener rules, custom CloudWatch metrics, or
task health check tuning.

=item * Some out of band infrastructure changes may break the ability
to re-run C<app-FargateStack> without manually updating the
configuration

=item * Support for only 1 EFS filesystem per task

=item * This framework assumes that the
L<operatingSystemFamily|https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters_ec2.html#runtime-platform_ec2>
is "LINUX" and the C<cpuArchitecture> is "X86_64" LINUX. This is
unlikely to change.

=back

=head1 TROUBLESHOOTING

=head2 Warning: task placed in a public subnet

lib/App/FargateStack/Pod.pm  view on Meta::CPAN

Manager, and related services to avoid needing internet access
altogether.

=back

=head3 Note on Subnet Selection

C<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.

=head2 My task failed to start and the reason is unclear

This is one of the most common and frustrating scenarios when working
with Fargate. You run C<start-service> or C<run-task>, the command
seems to succeed, but then the task quickly stops. The C<status>
command shows the desired count is 1 but the running count is 0, and
the logs are empty.

share/README.md  view on Meta::CPAN

  * [USING EXISTING BUCKETS](#using-existing-buckets)
* [HTTP SERVICES](#http-services)
  * [Overview](#overview)
  * [Key Assumptions When Creating HTTP Services](#key-assumptions-when-creating-http-services)
  * [Architecture](#architecture)
  * [Behavior by Task Type](#behavior-by-task-type)
  * [ACM Certificate Management](#acm-certificate-management)
  * [Port and Listener Rules](#port-and-listener-rules)
  * [Example Minimal Configuration](#example-minimal-configuration)
  * [Application Load Balancer](#application-load-balancer)
    * [Why Does the Framework Force the Use of a Load Balancer?](#why-does-the-framework-force-the-use-of-a-load-balancer)
  * [AWS WAF Support](#aws-waf-support)
    * [Enabling WAF Protection](#enabling-waf-protection)
    * [Configuring Managed Rules](#configuring-managed-rules)
      * [Rule Set Keywords](#rule-set-keywords)
      * [Rule Bundles](#rule-bundles)
    * [The Bootstrap Process (First Run)](#the-bootstrap-process-first-run)
    * [Ongoing Management (Subsequent Runs)](#ongoing-management-subsequent-runs)
    * [Conflict and Drift Management](#conflict-and-drift-management)
    * [Estimated Cost](#estimated-cost)
  * [Roadmap for HTTP Services](#roadmap-for-http-services)

share/README.md  view on Meta::CPAN

    # Dry-run and analyze the configuration
    app-FargateStack plan -c my-stack.yml

    # Provision the full stack
    app-FargateStack apply -c my-stack.yml

[Back to Table of Contents](#table-of-contents)

# DESCRIPTION

**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

share/README.md  view on Meta::CPAN

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](https://github.com/rlauer6/App-FargateStack/issues).

## Caveats

- The documentation may be incomplete or inaccurate.
- Features may change, and new ones will be added. See the
["ROADMAP"](#roadmap) for details.
- Deploying resources using this framework may result in AWS charges.
- This software is provided "as is", without warranty of any kind.
Use at your own risk.

## Features

- Minimal configuration: launch a Fargate service with just a task name
and container image
- Supports multiple task types: HTTP, HTTPS, daemon, cron (scheduled)
- Automatic resource provisioning: IAM roles, log groups, target groups,
listeners, etc.

share/README.md  view on Meta::CPAN

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.
- (7) Use `--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.
- (8) To speed up processing and avoid unnecessary API calls the
framework considers the configuration file the source of truth and a
reliable representation of the state of the stack. If you want to
re-sync the configuration file set `--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`.
- (9) `--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`.
- (10) Set `--route53-profile` to the profile that has
permissions to manage your hosted zones. By default the script will
use the default profile.
- (11) Deleting a task, daemon, or http service will delete all of
the resources associated with that task.

share/README.md  view on Meta::CPAN

        In this case, your scaling policy `max_capacity` value must be at least
        4.

[Back to Table of Contents](#table-of-contents)

# OVERVIEW

_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:

- ...build internal or external facing HTTP services that:
    - ...automatically provision certificates for external facing web applications
    - ...use an existing or create a new internal or external facing application load balancer (ALB).
    - ...automatically create an alias record in Route 53 for your domain
    - ...create a listener rule to redirect port 80 requests to 443 
- ...create queues and buckets to support your application
- ...use a dryrun mode to report the resources that will be built
before building them
- ...run `app-FargateStack` multiple times (idempotency)

share/README.md  view on Meta::CPAN

- support for scheduled and metric based [autoscaling](#autoscaling)

## Minimal Configuration

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:

- Create a Docker image that implements our worker, web app or API
- Create a minimal configuration file that describes our application
- Execute the framework's script and create the necessary AWS infrastructure
- Launch the http server, daemon, scheduled job, or adhoc worker

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"](#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:

- a cluster named `my-stack-cluster`
- a security group for the cluster
- an IAM role for the the cluster
- an IAM  policy that has permissions enabling your worker
- an ECS task definition that describes your task
- a CloudWatch log group
- an EventBridge target event
- an IAM role for EventBridge
- an IAM policy for EventBridge

share/README.md  view on Meta::CPAN

- An ALB if one is not detected

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 Changing Resources

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.

    my-worker:
      image: my-worker:latest
      command: /usr/local/bin/my-worker.pl
      type: task
      schedule: cron(00 15 * * * *)   
      bucket:
        name: my-worker-bucket

share/README.md  view on Meta::CPAN

        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.

## Configuration as State

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

[Back to Table of Contents](#table-of-contents)

# CLI OPTION DEFAULTS

share/README.md  view on Meta::CPAN


Only explicitly provided CLI options are tracked. Values derived from
environment variables or configuration files are not saved.

This feature is enabled by default.

[Back to Table of Contents](#table-of-contents)

# COMMAND LIST

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.

## Configuration File Naming

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

share/README.md  view on Meta::CPAN


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

    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

share/README.md  view on Meta::CPAN


- This command generates config; it does not deploy. Run your normal "plan/apply"
flow after reviewing the YAML.
- For HTTP/HTTPS, `domain:` is required at creation time in this shorthand.
- Always quote `schedule:...` to avoid shell interpretation of parentheses.

### deploy-service

    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-FargateTask deploy-service service-name

If you want to start multiple tasks for the service, you can include a
count argument:

share/README.md  view on Meta::CPAN

    CPU
    Start Time
    Elapsed Time
    Stopped Reason

### list-zones

    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

    logs start-time end-time

share/README.md  view on Meta::CPAN

configuration file.

### tasks

Displays a table that summarizes your stack resources.

### update-policy

    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.

share/README.md  view on Meta::CPAN


For example, given the following configuration:

    app:
      name: my-stack
    ...
    tasks:
      apache:
        type: https

The framework will:

- ...create a log group named /ecs/my-stack
- ...configure the apache task to write log streams with a prefix
like my-stack/apache/\*

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:

share/README.md  view on Meta::CPAN


- You can change the retention period by updating the
configuration file and re-running `apply`.
- To retain logs indefinitely, remove the `retention_days`
entry in your configuration file.

[Back to Table of Contents](#table-of-contents)

# IAM PERMISSIONS

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 policy is updated when new resources are

share/README.md  view on Meta::CPAN

`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.

## Task Execution Role vs. Task Role

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.

[Back to Table of Contents](#table-of-contents)

# SECURITY GROUPS

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

share/README.md  view on Meta::CPAN

      mount_point: /mnt/my-stack
      path: /
      readonly:

Acceptable values for `readonly` are "true" and "false".

## Field Descriptions

- 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.

share/README.md  view on Meta::CPAN

    Optional. Set to `true` to mount the EFS as read-only. Defaults to
    `false`.

## Additional Notes

- The ECS role's policy for your task is automatically modified
to allow read/write EFS access. Set `readonly:` in your task's
`efs:` section to "true" if only want read support.
- Your EFS security group must allow access from private subnets
where the Fargate tasks are placed.
- No changes are made to the EFS security group; the framework
assumes access is already configured
- Only one EFS volume is currently supported per task configuration.
- EFS volumes are task-scoped and reused only where explicitly configured.
- The framework does not automatically provision an EFS
filesystem for you. The framework does however validate that the
filesystem exists in the current account and region.

[Back to Table of Contents](#table-of-contents)

# CONFIGURATION

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`.

## GETTING STARTED

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.

share/README.md  view on Meta::CPAN

      my-stack-daemon:
        image: my-stack-daemon:latest
        type: daemon

This file contains the three key pieces of information you provided:
the application name, the task name, and the image to use.

### Step 2: Plan the Deployment (Dry Run)

Next, run the `plan` command. This is a crucial step that acts as a
dry run. The framework will:

- Read your minimal configuration file.
- Intelligently discover resources in your AWS account (like your VPC and subnets).
- Determine what new resources need to be created (like IAM roles, a security group, an ECS cluster and a CloudWatch log group).
- Report a full plan of action without making any actual changes.
- Update your configuration file with the discovered values and
sensible defaults.

    app-FargateStack plan

share/README.md  view on Meta::CPAN

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

## VPC AND SUBNET DISCOVERY

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:

- It is attached to an Internet Gateway (IGW)
- It has at least one available NAT Gateway

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.

## SUBNET SELECTION

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.

### Task placement and Availability Zones

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.

share/README.md  view on Meta::CPAN

      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

## FULL SCHEMA OVERVIEW

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:

share/README.md  view on Meta::CPAN

         target_group_arn:
         target_group_name:
         task_definition_arn:
         type:            # required (daemon, task, http, https)
     vpc_id:

[Back to Table of Contents](#table-of-contents)

# TASK SIZE

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.

[Back to Table of Contents](#table-of-contents)

# ENVIRONMENT VARIABLES

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.

## BASIC USAGE

    task:
      apache:

share/README.md  view on Meta::CPAN

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.

[Back to Table of Contents](#table-of-contents)

# SQS QUEUES

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._

## BASIC CONFIGURATION

share/README.md  view on Meta::CPAN

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`).

## DEFAULT QUEUE ATTRIBUTES

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

share/README.md  view on Meta::CPAN

## IAM POLICY UPDATES

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).

[Back to Table of Contents](#table-of-contents)

# SCHEDULED JOBS

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 job is defined like any other task, using `type: task`, and
adding a `schedule:` key in AWS EventBridge cron format.

## SCHEDULING A JOB

To schedule a job, add a `schedule:` key to your task definition. The
value must be a valid AWS cron expression, such as:

share/README.md  view on Meta::CPAN

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 "&lt;task>-schedule".

All scheduled tasks support environment variables, secrets, and other
standard task features.

## RUNNING AN ADHOC JOB

You can run a scheduled (or unscheduled) task manually at any time using:

share/README.md  view on Meta::CPAN

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.

[Back to Table of Contents](#table-of-contents)

# S3 BUCKETS

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._

share/README.md  view on Meta::CPAN

The `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.

## IAM-BASED ENFORCEMENT

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.

## USING EXISTING BUCKETS

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:

- The IAM role created by the framework may permit access to a path
- But a bucket policy with an explicit `Deny` will override that and block access
- This restriction will only be discovered at runtime when your task attempts access

To avoid surprises, ensure that any bucket policy on an external bucket
permits access from the IAM role you're configuring.

[Back to Table of Contents](#table-of-contents)

# HTTP SERVICES

share/README.md  view on Meta::CPAN

_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.

## ACM Certificate Management

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.

## Port and Listener Rules

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".

share/README.md  view on Meta::CPAN

    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:

- ...determining your VPC's ID
- ...identifying the private subnet IDs
- ...determining if there is and existing load balancer with the
correct scheme
- ...finding your load balancer's security group (if an ALB exists)

share/README.md  view on Meta::CPAN

discovered to be missing from your environment.

## Application Load Balancer

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.

### Why Does the Framework Force the Use of a Load Balancer?

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.

## AWS WAF Support

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.

### Enabling WAF Protection

To enable WAF, simply add a `waf` block with `enabled: true` to your
`alb` configuration:

    alb:
      # ... existing alb configuration ...
      waf:
        enabled: true

### Configuring Managed Rules

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.

#### Rule Set Keywords

- **base**: A strong baseline including `AWSManagedRulesCommonRuleSet`, `AWSManagedRulesAmazonIpReputationList`, and `AWSManagedRulesKnownBadInputsRuleSet`.
- **admin**: Protects exposed administrative pages (`AWSManagedRulesAdminProtectionRuleSet`).
- **sql**: Protects against SQL injection attacks (`AWSManagedRulesSQLiRuleSet`).
- **linux**: Includes rules for Linux and Unix-like environments.

share/README.md  view on Meta::CPAN

#### Rule Bundles

- **default**: Includes `base` and `sql`. This is the recommended starting point for most applications.
- **linux-app**: Includes `default` and `linux`.
- **wordpress-app**: Includes `default`, `linux`, and `wordpress`.
- **windows-app**: Includes `default` and `windows`.
- **all**: Includes all standard, non-premium rule sets. **Warning:** This will likely exceed the default WCU quota and may incur additional costs.

### The Bootstrap Process (First Run)

On the first `apply` run with WAF enabled, the framework will perform
a one-time bootstrap:

1. It generates a default `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.
2. It calls `aws wafv2 create-web-acl` to create a new Web ACL.
3. It calls `aws wafv2 associate-web-acl` to link the new Web ACL to
your Application Load Balancer.
4. It updates your configuration file with the state of the new
WAF resources, including its Name, ID, ARN, LockToken, and a checksum

share/README.md  view on Meta::CPAN

- If the checksum has not changed, it will skip the update to avoid
unnecessary API calls.

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.

### Conflict and Drift Management

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.

### Estimated Cost

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:

- **$5.00 / month** for the Web ACL itself.
- **$4.00 / month** for the four AWS Managed Rule Groups included
in the `default` bundle (3 in 'base', 1 in 'sql').

share/README.md  view on Meta::CPAN


# AUTOSCALING

## Overview

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.

## Enabling Autoscaling

To enable autoscaling for a service, add an `autoscaling` block to its task
configuration in your .yml configuration file.

tasks:
  my-service:

share/README.md  view on Meta::CPAN

- **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.

## Example: Scaling on CPU Utilization

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%.

share/README.md  view on Meta::CPAN


- Stacks may contain multiple daemon services, but only one task
may be exposed as an HTTP/HTTPS service via an ALB.
- Limited configuration options for some resources such as
advanced load balancer listener rules, custom CloudWatch metrics, or
task health check tuning.
- Some out of band infrastructure changes may break the ability
to re-run `app-FargateStack` without manually updating the
configuration
- Support for only 1 EFS filesystem per task
- This framework assumes that the
[operatingSystemFamily](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters_ec2.html#runtime-platform_ec2)
is "LINUX" and the `cpuArchitecture` is "X86\_64" LINUX. This is
unlikely to change.

[Back to Table of Contents](#table-of-contents)

# TROUBLESHOOTING

## Warning: task placed in a public subnet

share/README.md  view on Meta::CPAN

and the subnet has a route to it.
- Alternatively, configure VPC endpoints for ECR, Secrets
Manager, and related services to avoid needing internet access
altogether.

### Note on Subnet Selection

`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.

## My task failed to start and the reason is unclear

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.



( run in 1.690 second using v1.01-cache-2.11-cpan-df04353d9ac )