Billy McMonagle
03/19/2026, 9:44 PMMarvin
03/19/2026, 9:44 PMMarvin
03/19/2026, 9:46 PMBilly McMonagle
03/19/2026, 9:48 PMMarvin
03/19/2026, 9:48 PMMarvin
03/19/2026, 9:51 PMprefect-aws ecs-worker deploy-events \
--work-pool-name "my-ecs-pool" \
--stack-name "my-ecs-pool-events" \
--existing-cluster-arn "arn:aws:ecs:us-east-1:123456789012:cluster/my-cluster" \
--region us-east-1
- This creates:
- SQS queue: my-ecs-pool-ecs-events (plus a DLQ with receive count 3)
- EventBridge rule: my-ecs-pool-ecs-task-events (ECS Task State Change events for your cluster → SQS)
- Source code for this command and resources:
- CLI: prefect_aws/_cli/ecs_worker.py
- EventBridge/SQS stack: prefect_aws/infra/worker/events_stack.py
2) Set the required environment variables on your worker task
- Add these to your ECS worker task definition/service:
PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_ENABLED=true
PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_SQS_QUEUE_NAME=my-ecs-pool-ecs-events
# Optional if queue is in a different/default region is ambiguous
PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_SQS_QUEUE_REGION=us-east-1
# Optional (enabled by default) – forwards CloudWatch logs from crashed runs
PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_FORWARD_CRASHED_RUN_LOGS=true
PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_FORWARD_CRASHED_RUN_LOGS_MAX_EVENTS=500
- The observer starts automatically within the ECS worker when these are set.
- Observer implementation: prefect_aws/observers/ecs.py
- Worker integration (auto-start): prefect_aws/workers/ecs_worker.py
Option B: Manual/IaC approach
- If you prefer CloudFormation/Terraform, create:
- SQS queue (name you’ll set in PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_SQS_QUEUE_NAME)
- Visibility timeout ~300s, message retention ~7 days, DLQ with maxReceiveCount=3
- Queue policy permitting events.amazonaws.com to SendMessage
- EventBridge rule matching:
{
"source": [
"aws.ecs"
],
"detail-type": [
"ECS Task State Change"
],
"detail": {
"clusterArn": [
"arn:aws:ecs:REGION:ACCOUNT:cluster/your-cluster"
]
}
}
Target the SQS queue
- Then set the same environment variables on the worker as in Option A.
Worker task role (IAM) permissions
- Ensure the ECS worker task role has these (min set to support observer + normal ECS operations):
```
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes",
"sqs:GetQueueUrl"
],
"Resource": "arnawssqsREGIONACCOUNT-ID:my-ecs-pool-ecs-events*"
},
{
"Effect": "Allow",
"Action": [
"ecs:DescribeTasks",
"ecs:DescribeTaskDefinition",
"ecs:ListTasks",
"ecs:RunTask",
"ecs:StopTask",
"ecs:DescribeClusters",
"ecs:ListClusters",
"ecs:RegisterTaskDefinition",
"ecs:DeregisterTaskDefinition",Marvin
03/19/2026, 9:51 PM- IAM source refs: <https://github.com/PrefectHQ/prefect-aws/blob/main/prefect_aws/infra/worker/service_stack.py|prefect_aws/infra/worker/service_stack.py>
How to test it
1) Verify infra
- SQS queue exists:
aws sqs list-queues --queue-name-prefix "my-ecs-pool-ecs-events" --region us-east-1
- EventBridge rule exists and targets the queue:
aws events describe-rule --name "my-ecs-pool-ecs-task-events" --region us-east-1
aws events list-targets-by-rule --rule "my-ecs-pool-ecs-task-events" --region us-east-1
```
2) Check worker logs for observer startup
- In CloudWatch logs for your worker container, you should see logs indicating the ECS observer started and is polling SQS.
3) Run a test flow and confirm state transitions
- Start a normal flow: it should move through InfrastructurePending → Running → Completed.
- Induce a pre-start failure (e.g., bad image or a flow that raises at import): the run should be marked Crashed and, if enabled, CloudWatch logs forwarded to the flow run.
Useful references
- ECS Observer code: prefect_aws/observers/ecs.py
- EventBridge/SQS stack: prefect_aws/infra/worker/events_stack.py
- ECS worker (observer auto-start integration): prefect_aws/workers/ecs_worker.py
- Prefect deployments in 3.x: Deployment overview
If you share your:
- Work pool name
- ECS cluster ARN
- AWS region
…I can generate copy-paste CLI and/or a minimal CloudFormation snippet tailored to your environment.Billy McMonagle
03/20/2026, 1:52 PMMarvin
03/20/2026, 1:52 PMBilly McMonagle
03/20/2026, 4:12 PMMarvin
03/20/2026, 4:12 PMMarvin
03/20/2026, 4:13 PMBilly McMonagle
03/20/2026, 4:14 PMBilly McMonagle
03/20/2026, 4:34 PMMarvin
03/20/2026, 4:34 PMMarvin
03/20/2026, 4:40 PMPREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_SQS_QUEUE_NAME. It’s safe to deploy alongside any existing ECS worker setup.
AWSTemplateFormatVersion: '2010-09-09'
Description: Prefect ECS Observer - events-only (EventBridge -> SQS)
Parameters:
ClusterArn:
Type: String
Description: ARN of the ECS cluster to observe
QueueName:
Type: String
Default: prefect-ecs-tasks-events
Description: Name for the SQS queue to receive ECS task state change events
Resources:
Dlq:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub '${QueueName}-dlq'
MessageRetentionPeriod: 1209600 # 14 days
EventsQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Ref QueueName
RedrivePolicy:
deadLetterTargetArn: !GetAtt Dlq.Arn
maxReceiveCount: 5
VisibilityTimeout: 30
MessageRetentionPeriod: 1209600
EcsTaskStateChangeRule:
Type: AWS::Events::Rule
Properties:
Name: !Sub 'prefect-ecs-task-events-${AWS::StackName}'
Description: Publishes ECS task state changes for Prefect observer
EventPattern:
source:
- aws.ecs
detail-type:
- ECS Task State Change
detail:
clusterArn:
- !Ref ClusterArn
State: ENABLED
Targets:
- Arn: !GetAtt EventsQueue.Arn
Id: SendToSqs
EventsQueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
Queues:
- !Ref EventsQueue
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowEventBridgeToSend
Effect: Allow
Principal:
Service: <http://events.amazonaws.com|events.amazonaws.com>
Action: SQS:SendMessage
Resource: !GetAtt EventsQueue.Arn
Condition:
ArnEquals:
aws:SourceArn: !GetAtt EcsTaskStateChangeRule.Arn
Outputs:
QueueName:
Value: !GetAtt EventsQueue.QueueName
QueueUrl:
Value: !Ref EventsQueue
QueueArn:
Value: !GetAtt EventsQueue.Arn
Worker configuration (env + IAM)
- Set these on your ECS worker TaskDefinition (container env):
- PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_ENABLED=true (default is true)
- PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_SQS_QUEUE_NAME=<your queue name>
- Optional: PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_SQS_QUEUE_REGION=<region>
- Optional: PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_FORWARD_CRASHED_RUN_LOGS=true and PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_FORWARD_CRASHED_RUN_LOGS_MAX_EVENTS=500
- Add these permissions to the worker task role (restrict Resource to your queue ARN(s)):
```
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EcsObserverSqsReceive",
"Effect": "Allow",
"Action": [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes",
"sqs:GetQueueUrl"
],
"Resource": "arnawssqs<region><account-id>:<queue-name>"
},
{Marvin
03/20/2026, 4:40 PMOption B: CDK (events-only)
A minimal CDK example in TypeScript that mirrors the CFN above:
import * as cdk from 'aws-cdk-lib';
import { aws_sqs as sqs, aws_events as events, aws_events_targets as targets, aws_iam as iam } from 'aws-cdk-lib';
export interface EcsObserverEventsStackProps extends cdk.StackProps {
clusterArn: string;
queueName?: string;
}
export class EcsObserverEventsStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props: EcsObserverEventsStackProps) {
super(scope, id, props);
const dlq = new sqs.Queue(this, 'Dlq', {
queueName: ${props.queueName ?? 'prefect-ecs-tasks-events'}-dlq,
retentionPeriod: cdk.Duration.days(14),
});
const queue = new sqs.Queue(this, 'EventsQueue', {
queueName: props.queueName ?? 'prefect-ecs-tasks-events',
visibilityTimeout: cdk.Duration.seconds(30),
retentionPeriod: cdk.Duration.days(14),
deadLetterQueue: { queue: dlq, maxReceiveCount: 5 },
});
const rule = new events.Rule(this, 'EcsTaskStateChangeRule', {
description: 'Publishes ECS task state changes for Prefect observer',
eventPattern: {
source: ['aws.ecs'],
detailType: ['ECS Task State Change'],
detail: { clusterArn: [props.clusterArn] },
},
});
rule.addTarget(new targets.SqsQueue(queue));
queue.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [new iam.ServicePrincipal('events.amazonaws.com')],
actions: ['sqs:SendMessage'],
resources: [queue.queueArn],
conditions: { ArnEquals: { 'aws:SourceArn': rule.ruleArn } },
}));
}
}
```
Need the full worker + observer as IaC?
- Prefect’s ECS worker service stack (which includes the observer infra, worker task definition, IAM, autoscaling, and logging) is maintained in the Prefect AWS integration. If you’d like, I can export the exact CloudFormation template we ship and paste it here, or tailor it to your VPC/cluster.
- Source entry points you can reference:
- Observer code and settings:
- ECS Observer
- Observer settings
- Package root:
- prefect_aws
Quick check questions so I can tailor the template for you
- Do you want events-only (to plug into an existing worker) or a full worker service + observer?
- Fargate or EC2 launch type?
- Your ECS cluster ARN, VPC ID, and private subnet IDs?
- Desired worker scaling bounds (min/max tasks) and container sizing (CPU/memory)?
- Prefect Cloud or self-hosted (so I can wire in the right auth env)?
If you share those, I’ll generate a CloudFormation/CDK stack that you can drop directly into your account.Billy McMonagle
03/20/2026, 8:13 PMMarvin
03/20/2026, 8:13 PMMarvin
03/20/2026, 8:14 PMreplicate_ecs_event in ecs.py
- Bridges gaps before a flow connects
- For tasks that crash before the flow code can connect to Prefect, the observer still records what happened and surfaces details.
- Detects crashes and updates run state when appropriate
- If a task transitions to STOPPED with a non‑zero exit code or a failure reason and the corresponding Prefect run has not already reached a terminal state, the observer marks the run as Crashed.
- It attaches diagnostic details (exit code, reason) and can fetch a slice of recent CloudWatch logs to include with the crash for faster triage.
- References: mark_runs_as_crashed, fetch_cloudwatch_logs in ecs.py
- Optionally performs cleanup
- Can deregister task definitions on STOPPED if configured.
- Reference: deregister_task_definition in ecs.py
- Safe-by-default behavior
- It will not override states that your flow has already reported (e.g., if the flow has already completed/failed and reported its terminal state, the observer won’t change it).
- It focuses on enriching observability and handling “crashed before connect” scenarios.
- Robust processing of the SQS stream
- Long-polls SQS, applies filtering (by lastStatus and Prefect-specific tags), retries with backoff on transient errors, acknowledges messages on success, and relies on a DLQ after max receives.
- References: SqsSubscriber and retry/backoff constants in ecs.py
Config knobs you can use
- Enable/disable and tune behavior via environment variables:
- PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_ENABLED (default true)
- PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_SQS_QUEUE_NAME (required for events-only setups)
- PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_SQS_QUEUE_REGION (optional if cross-region)
- PREFECT_INTEGRATIONS_AWS_ECS_OBSERVER_FORWARD_CRASHED_RUN_LOGS and ..._MAX_EVENTS
- Reference: prefect_aws.settings
If you’d like, I can tailor a minimal “events-only” stack (SQS + EventBridge) or a full worker+observer stack for your cluster/VPC and include the exact env and IAM you’ll need.