New to Kestra?
Use blueprints to kickstart your first workflows.
Audit CloudFormation StackStatus for failed or rolled-back stacks and alert Slack before a stuck deployment blocks the next one.
A CloudFormation stack that fails to create or update rolls back to a
terminal state like ROLLBACK_COMPLETE or UPDATE_ROLLBACK_FAILED and
then simply sits there - no new deployment can proceed against it until
someone manually deletes or fixes it, and nothing in CloudFormation
itself proactively announces that a stack is stuck. This blueprint reads
a stack's own StackStatus and StackStatusReason on a schedule and
alerts the moment it lands in one of CloudFormation's own documented
failure or rollback states.
This blueprint was created by Mohit Jeswani.
periodic_stack_check (io.kestra.plugin.core.trigger.Schedule) runs every 30 minutes, explicitly passing aws_region, stack_name, and aws_endpoint_override through its own inputs: block so the flow is self-contained when the trigger fires. Shipped disabled so a manual run can be verified first.probe_stack_status (io.kestra.plugin.aws.cli.AwsCLI) runs aws cloudformation describe-stacks --query 'Stacks[0]' and captures the result's fields (StackStatus, StackStatusReason, and every other field CloudFormation returns) as named task outputs via the plugin's own documented output-echo convention.evaluate_stack_health (io.kestra.plugin.core.flow.If) branches on whether StackStatus is one of CloudFormation's own documented terminal failure or rollback values.then (NEEDS_ATTENTION): alert_stack_needs_attention reports the status and StackStatusReason to Slack.else (HEALTHY or IN_PROGRESS): log_stack_healthy records the current status.log_audit_complete always runs last, printing the stack's status regardless of which branch fired.errors block alerts Slack separately if the flow itself fails outright - an unreachable endpoint, a missing stack, or bad credentials must not read as either handled state.StackStatus, not inferred from a deploy pipeline's own exit code, which never sees a stack that failed and rolled back cleanly.StackStatusReason delivered straight to Slack, instead of requiring someone to open the CloudFormation console to read why a stack is stuck.io.kestra.plugin.aws.cli.AwsCLI driving a read-only audit query with --query and capturing structured output via the plugin's own output-echo convention, not just running a provisioning command.endpointOverride, with no AWS account or CloudFormation drift-detection feature (which LocalStack does not implement, even on paid tiers) required.ROLLBACK_COMPLETE or a _FAILED state and blocks the next deployment.StackStatusReason instead of opening the CloudFormation console to find out why a stack is stuck.AwsCLI used for a read-only governance probe, paired with its documented structured-output convention.CloudFormation itself has no scheduler and no "this stack has been stuck for a while" alert - a failed rollback sits silently until the next deploy attempt trips over it. Kestra supplies the schedule, the HEALTHY/IN_PROGRESS/NEEDS_ATTENTION classification as a first-class branch, and an execution history that shows exactly when a stack crossed into a failure state and what CloudFormation's own reason string said.
stack_name).cloudformation:DescribeStacks permission.Local testing (LocalStack, no AWS account needed):
docker run -d --name localstack -p 4566:4566 localstack/localstack:latest
docker network connect YOUR_KESTRA_NETWORK localstack
aws --endpoint-url=http://localhost:4566 cloudformation create-stack --stack-name test-stack --template-body '{"Resources":{"Bucket":{"Type":"AWS::S3::Bucket"}}}'
Run docker network ls first to find the active Kestra network name. Port 4566 is LocalStack's single edge port for every emulated AWS service and does not collide with Kestra's own UI on port 8080. From inside the Kestra worker's Docker network, set aws_endpoint_override to http://localstack:4566 instead of http://localhost:4566. describe-stacks (used by this blueprint) is implemented by LocalStack's free Community edition; CloudFormation drift detection (detect-stack-drift) is not implemented by LocalStack at all, which is why this blueprint audits StackStatus instead.
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY: credentials for io.kestra.plugin.aws.cli.AwsCLI. Against LocalStack, any non-empty placeholder value works (LocalStack Community does not validate credentials) - the well-known convention is literally the strings test / test.SLACK_WEBHOOK_URL: Slack incoming webhook used by alert_stack_needs_attention and the errors block.SECRET_, base64-encoded - for example SECRET_AWS_ACCESS_KEY_ID=$(echo -n 'test' | base64) - and read back in flows with {{ secret('NAME') }}.| Input | Type | Default | Purpose |
|---|---|---|---|
aws_region |
String | us-east-1 |
Region passed to AwsCLI as AWS_DEFAULT_REGION |
stack_name |
String | <your-cloudformation-stack-name> |
Name or ARN of the CloudFormation stack to audit |
aws_endpoint_override |
String | http://localhost:4566 |
LocalStack endpoint override; clear for production AWS |
outputs.probe_stack_status.vars.StackStatus / StackStatusReason: the two fields this blueprint classifies on.outputs.probe_stack_status.vars.*: every other field CloudFormation's describe-stacks returns for the stack (StackId, CreationTime, Outputs, Parameters, DriftInformation, and more) is captured the same way, even though only the two above are read here.test-stack with the commands above.AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY (test / test against LocalStack) and SLACK_WEBHOOK_URL secrets.stack_name: test-stack and confirm log_stack_healthy fires with CREATE_COMPLETE.ROLLBACK_COMPLETE, then re-run to confirm alert_stack_needs_attention fires with the real StackStatusReason.aws_endpoint_override and point this at a real AWS account and stack once you trust the check; enable periodic_stack_check once you trust the cadence.stack_name values with io.kestra.plugin.core.flow.Loop to audit every critical stack in an account in one run.io.kestra.plugin.aws.cli.AwsCLI task running aws cloudformation delete-stack behind an explicit dry_run/approval gate for stacks stuck in ROLLBACK_COMPLETE (CloudFormation's own documented recovery path for that specific state - a stack in ROLLBACK_COMPLETE can only be deleted, never updated again).detect-stack-drift / describe-stack-drift-detection-status for configuration drift, since that governance angle is real but not exercisable locally.detect-stack-drift and describe-stack-drift-detection-status are absent from its supported operations. That is specifically why this blueprint audits StackStatus (which LocalStack's describe-stacks does support) instead of drift.aws_endpoint_override always sets AWS_ENDPOINT_URL, even when empty. This blueprint defaults it to a LocalStack URL for local testing; for production AWS, clear the input value rather than leaving it pointed at a local address, since an unreachable AWS_ENDPOINT_URL fails every call.tr -d '\n' | xargs -0 -I {} echo '::{"outputs":{}}::') is copied from io.kestra.plugin.aws.cli.AwsCLI's own Javadoc example, but with spaces no longer stripped. The plugin's own example additionally does tr -d ' \n', which would corrupt StackStatusReason's human-readable sentence by deleting every space in it.ROLLBACK_COMPLETE cannot be updated, only deleted, per CloudFormation's own documented behavior - log_stack_healthy/alert_stack_needs_attention only report the state; this blueprint does not attempt that recovery itself (see How to extend).StackStatus enum values and the StackStatusReason field are taken verbatim from AWS CloudFormation's own Stack API reference, and LocalStack's drift-detection gap is taken from LocalStack's own published supported-operations documentation - but no Docker container or Kestra engine was run to execute this flow end to end.