Schedule icon
AwsCLI icon
If icon
SlackIncomingWebhook icon
Log icon

ACM Certificate Expiry Compliance Sentinel

Audit AWS Certificate Manager for certificates expiring soon and alert Slack with domain and renewal status before an outage.

Categories
Infrastructure

An ACM certificate that expires without being renewed - because it was imported rather than ACM-issued, because a domain validation CNAME quietly disappeared, or because the resource it protects was never associated for auto-renewal eligibility - takes down every service behind it with a hard TLS handshake failure, often noticed only once customers start reporting broken connections. This blueprint lists every issued certificate on a schedule and alerts Slack with the exact domain, expiry date, and auto-renewal eligibility for anything expiring soon.

How it works

  1. periodic_expiry_check (io.kestra.plugin.core.trigger.Schedule) runs once a day, explicitly passing aws_region and warning_threshold_days 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.
  2. probe_list_certificates (io.kestra.plugin.aws.cli.AwsCLI) runs aws acm list-certificates --certificate-statuses ISSUED, capturing the full CertificateSummaryList - domain, NotAfter, and RenewalEligibility for every issued certificate in one call.
  3. evaluate_certificate_expiry (io.kestra.plugin.core.flow.If) branches on whether any certificate's NotAfter falls within warning_threshold_days of now (or has already passed).
    • then (CERTIFICATES_EXPIRING_SOON): alert_certificates_expiring_soon lists every flagged certificate's domain, expiry date, and renewal eligibility to Slack.
    • else (HEALTHY): log_certificates_healthy records the clean pass.
  4. log_audit_complete always runs last, printing the total issued-certificate count regardless of which branch fired.
  5. The errors block alerts Slack separately if the flow itself fails outright - an unreachable ACM API or invalid credentials.

What you get

  • A compliance answer computed from ACM's own NotAfter and RenewalEligibility fields in a single list-certificates call, not from a manually maintained spreadsheet of certificate expiry dates.
  • Renewal eligibility surfaced directly in the alert text - an INELIGIBLE certificate will not be auto-renewed by ACM at all and needs immediate manual action, which is a materially more urgent case than an ELIGIBLE one that should auto-renew on its own.
  • A Resource Quota / Compliance Sentinel for a failure mode ACM itself does not proactively push to Slack - ACM sends renewal reminder emails to the account's registered contacts, but nothing to a team's own alerting channel.
  • A worked example of io.kestra.plugin.aws.cli.AwsCLI combined with jq's real fromdateiso8601 and now builtins for date-window filtering, with no prior blueprint coverage of this plugin surface in this repository.

Who it's for

  • Platform and security teams who own TLS certificate hygiene across an AWS account and want one Slack alert instead of auditing the ACM console on a schedule.
  • Teams running a mix of ACM-issued and manually imported certificates who need the renewal-eligibility distinction surfaced automatically, since imported certificates silently never auto-renew.
  • Anyone evaluating the AWS plugin who wants a worked example of AwsCLI combined with client-side jq date-window filtering, a pattern with no prior example in this repository.

Why orchestrate this with Kestra

ACM's own expiry notifications go to the account's registered contact emails, not to a team's operational alerting channel, and there is no built-in per-team Slack integration or configurable warning window. Kestra supplies the schedule, the HEALTHY/CERTIFICATES_EXPIRING_SOON classification as a first-class branch, a configurable warning window, and an execution history showing exactly when a certificate first entered the warning window.

Prerequisites

  • An AWS account with at least one ACM certificate in ISSUED status (update aws_region; CloudFront-associated certificates must be requested in us-east-1 regardless of where the protected resource lives).
  • AWS credentials with acm:ListCertificates permission - entirely read-only, cannot request, import, delete, or renew any certificate.
  • A Slack incoming webhook for alerts.

Local testing: LocalStack supports ACM's list-certificates operation (confirmed via LocalStack's own ACM documentation), letting a self-created local certificate exercise this flow's branching logic end to end. LocalStack now requires its own account and auth token as of March 2026 regardless, and a LocalStack-created certificate's RenewalEligibility/auto-renewal behavior may not fully mirror AWS's own production renewal engine.

Secrets

  • AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY: credentials for io.kestra.plugin.aws.cli.AwsCLI.
  • SLACK_WEBHOOK_URL: Slack incoming webhook used by alert_certificates_expiring_soon and the errors block.
  • In Kestra OSS (no Enterprise secrets backend), secrets are supplied as environment variables prefixed SECRET_, base64-encoded - for example SECRET_AWS_SECRET_ACCESS_KEY=$(echo -n '<your-secret-key>' | base64) - and read back in flows with {{ secret('NAME') }}.

Inputs

Input Type Default Description
aws_region STRING us-east-1 AWS region whose issued certificates are audited
warning_threshold_days INT 30 Expiry window in days triggering CERTIFICATES_EXPIRING_SOON

Outputs

  • outputs.probe_list_certificates.vars.CertificateSummaryList (list): every issued certificate's CertificateArn, DomainName, NotAfter, RenewalEligibility, and other summary fields (verified from list-certificates's own documented output schema).

Quick start

  1. Confirm at least one ACM certificate is ISSUED in the target account/region.
  2. Add the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and SLACK_WEBHOOK_URL secrets.
  3. Run the flow manually and confirm log_certificates_healthy fires if nothing is near expiry.
  4. Temporarily raise warning_threshold_days above the real distance to a certificate's expiry (or request a short-lived test certificate) and re-run to confirm alert_certificates_expiring_soon fires with the correct domain and renewal eligibility.
  5. Enable periodic_expiry_check once you trust the check against your real account.

How to extend

  • Loop over several aws_region values with io.kestra.plugin.core.flow.Loop to audit every region a team operates ACM certificates in, in one run.
  • Split the alert into two severities by nesting a second If on a shorter critical_threshold_days, so an already-expired or imminently-expiring certificate pages differently than one merely inside the 30-day window.
  • Cross-reference RenewalEligibility: INELIGIBLE certificates against aws acm describe-certificate's DomainValidationOptions in a follow-up task, to surface exactly which validation record lapsed.

Pitfalls

  • RenewalEligibility: ELIGIBLE does not guarantee a successful auto-renewal - ACM's automatic renewal can still fail silently if DNS/email validation lapses; this sentinel treats both eligibility states as worth alerting on, just with different urgency framed in the Slack text.
  • list-certificates is paginated - a very large certificate inventory may not return every certificate in a single call; this blueprint's single call only sees the first page, unlike this repository's other AwsCLI sentinels that filter server-side to a small result set.
  • CloudFront-associated certificates must live in us-east-1 - running this sentinel against a different aws_region will simply not see them; audit us-east-1 separately if any certificates protect CloudFront distributions.
  • This blueprint is UNTESTED against a live AWS account. The CLI syntax and the NotAfter/RenewalEligibility output fields are taken verbatim from AWS's own CLI reference documentation, and fromdateiso8601/now are real jq builtins - but no Kestra engine was run to execute this flow end to end.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.