New to Kestra?
Use blueprints to kickstart your first workflows.
Audits AWS CloudWatch Log Groups configured with indefinite retention, calculating storage waste and alerting FinOps teams on Slack.
When serverless AWS Lambda functions, Amazon ECS tasks, or Elastic Load Balancers generate logs, AWS automatically provisions dedicated CloudWatch Log Groups. By default, Amazon CloudWatch assigns an indefinite retention policy (Never Expire) to every newly created log group.
Over months of production releases, debug logs, health checks, and historical traces accumulate without expiration. Because CloudWatch standard log storage costs approximately $0.03 per gigabyte per month, enterprise accounts frequently accumulate terabytes of obsolete log data, causing monthly AWS infrastructure bills to inflate silently.
This blueprint provides an automated cloud FinOps sentinel. Running on a weekly schedule, it scans all CloudWatch Log Groups in the target region, flags groups lacking expiration periods, calculates total storage consumption and projected monthly waste, and delivers an actionable remediation report to Slack.
weekly_log_retention_audit trigger (io.kestra.plugin.core.trigger.Schedule) initiates the audit every Monday at 08:00 UTC.scan_log_group_retention task (io.kestra.plugin.scripts.python.Script) queries the CloudWatch Logs API to inspect retentionInDays across all regional log groups and compiles cloudwatch_retention_report.json.check_unmanaged_log_groups flowable task (io.kestra.plugin.core.flow.If) branches based on whether any log groups are set to never expire.notify_slack_finops (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) delivers an actionable triage card detailing the primary offending group and the exact put-retention-policy CLI command.log_compliant_retention records compliant status.export_retention_manifest task records execution metadata for FinOps chargeback dashboards.flowchart TD
A[Schedule: Weekly Monday 08:00 UTC] --> B[scan_log_group_retention: AWS CloudWatch Logs API]
B --> C{Unmanaged Groups Detected?}
C -- Yes --> D[notify_slack_finops: Slack Webhook]
C -- No --> E[log_compliant_retention: Log]
D --> F[export_retention_manifest: Return JSON]
E --> F
| Name | Type | Default | Description |
|---|---|---|---|
aws_region |
STRING | us-east-1 |
AWS region to inspect for CloudWatch Log Groups. |
recommended_retention_days |
INT | 30 |
Target retention policy duration in days. |
log_storage_cost_per_gb_month |
FLOAT | 0.03 |
Estimated CloudWatch log storage pricing per GB per month. |
slack_channel |
STRING | #finops-alerts |
Slack channel destination for cost notifications. |
{{ outputs.scan_log_group_retention.vars.has_unmanaged_groups }}: Boolean flag indicating if unmanaged groups were found.{{ outputs.scan_log_group_retention.vars.unmanaged_count }}: Total number of log groups lacking retention limits.{{ outputs.scan_log_group_retention.vars.total_wasted_gb }}: Total accumulated storage volume in gigabytes.{{ outputs.scan_log_group_retention.vars.total_monthly_spend_usd }}: Projected monthly spend on unmanaged storage.{{ outputs.scan_log_group_retention.outputFiles['cloudwatch_retention_report.json'] }}: Structured JSON report.logs:DescribeLogGroups.AWS_ACCESS_KEY_ID: AWS IAM access key identifier.AWS_SECRET_ACCESS_KEY: AWS IAM secret access key.SLACK_WEBHOOK_URL: Slack Incoming Webhook endpoint URL.SLACK_WEBHOOK_URL in your Kestra namespace secrets.aws_region.nextToken) when calling DescribeLogGroups to ensure complete coverage.boto3.client('logs').put_retention_policy() after human approval via io.kestra.plugin.core.flow.Pause.boto3.client('ec2').describe_regions() for multi-region governance.