New to Kestra?
Use blueprints to kickstart your first workflows.
Audit AWS KMS key rotation status, alert Slack when disabled, and optionally enable rotation with a verified fix.
A customer-managed KMS key with automatic rotation disabled keeps encrypting new data
under the same key material indefinitely, exactly the setting most security baselines
and compliance frameworks require enabled, and nothing about the key itself looks
different, encryption and decryption both keep working whether rotation is on or off.
get-key-rotation-status already reports the real KeyRotationEnabled value, this
blueprint is what reads it on a schedule and turns a disabled key into an alert, with a
narrow, single-key remediation path instead of leaving it as a standing finding.
route_connection_mode (io.kestra.plugin.core.flow.If) picks the AWS CLI endpoint
based on use_localstack. Both branches run the identical
aws kms get-key-rotation-status --key-id key_id via
io.kestra.plugin.aws.cli.AwsCLI, captured as named outputs through the plugin's own
documented ::{"outputs":{}}:: echo convention. The command's own JSON response is
already a flat object (KeyId, KeyRotationEnabled, and more), so no --query
reshaping is needed this time, only the connection differs between branches.classify_rotation_status (io.kestra.plugin.core.debug.Return) reads whichever
branch's vars.KeyRotationEnabled exists (guarded with is defined/is not defined)
and returns PROBE_FAILED when neither branch returned at all, COMPLIANT when it is
true, CRITICAL or WARNING depending on violation_severity when it is not.route_by_rotation_status (io.kestra.plugin.core.flow.Switch) branches on that
status: COMPLIANT logs it, WARNING, CRITICAL, and PROBE_FAILED each alert Slack.remediate_gate (io.kestra.plugin.core.flow.If) runs only when the key was not
COMPLIANT, auto_remediate is true, and dry_run is false:route_remediation_connection picks the same connection mode again, then calls
aws kms enable-key-rotation --key-id key_id on this one key, never a bulk call
across every key in the account.verify_rotation_* re-runs get-key-rotation-status, and report_remediation_result
(io.kestra.plugin.core.flow.If) fails the flow with
io.kestra.plugin.core.execution.Fail if it still does not report true, so a
failed enable is never silently reported as fixed.log_audit_summary always runs last and logs the final classification.errors block posts to Slack if the flow itself fails outside the gate logic above.::{"outputs":{}}:: convention applied to a command whose own JSON output is already a
flat object, no --query reshaping required.get-key-rotation-status is a single point-in-time read, KMS itself has no concept of a
schedule, a severity policy, or an alert built from what it finds. Kestra adds the
recurring check, the severity policy per key, the Slack alert, the single-key gated
remediation, and the verification that the fix worked, all as one declarative YAML file
you can version and review like code, runnable against LocalStack before it ever touches
a real key.
For local testing, a LocalStack container, no AWS account needed:
docker run --rm -d --name localstack -p 4566:4566 localstack/localstack
Port 4566 does not collide with Kestra's own default UI port. Both
GetKeyRotationStatus and EnableKeyRotation are implemented in LocalStack's community
edition.
For real AWS, an IAM credential with kms:GetKeyRotationStatus permission to read, and
additionally kms:EnableKeyRotation if auto_remediate will ever be used.
A Slack incoming webhook for alerts.
If Kestra itself runs in a container and use_localstack is true, point
localstack_endpoint at http://host.docker.internal:4566 or connect Kestra's
container to LocalStack's Docker network and use its container name instead, check the
exact network name first with docker network ls.
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: used by the *_live tasks only, not
needed when use_localstack is true, LocalStack accepts the fixed test/test
credentials this blueprint hard-codes on the LocalStack branches.
SLACK_WEBHOOK: Slack incoming webhook URL used by the alert tasks and the errors
block.
In Kestra OSS, set these as base64-encoded environment variables, for example
SECRET_AWS_ACCESS_KEY_ID, SECRET_SLACK_WEBHOOK.
| Input | Type | Default | Allowed values |
|---|---|---|---|
key_id |
STRING | <your-kms-key-id> |
any customer-managed KMS key ID or ARN |
aws_region |
STRING | us-east-1 |
any AWS region |
violation_severity |
SELECT | critical |
critical, warning |
auto_remediate |
BOOL | false |
true, false |
dry_run |
BOOL | true |
true, false |
use_localstack |
BOOL | true |
true, false |
localstack_endpoint |
STRING | http://localhost:4566 |
any LocalStack endpoint URL |
aws --endpoint-url=http://localhost:4566 kms create-key (note the returned KeyId).SLACK_WEBHOOK secret.key_id to that key and run this flow manually, it should report CRITICAL.auto_remediate: true and dry_run: false and run it again, it should enable
rotation, verify it, and report remediation_confirmed.use_localstack: false and add the AWS secrets to point this at a real key.daily trigger.io.kestra.plugin.core.flow.Loop to audit
every customer-managed key in an account from one flow, instead of one key per run.describe-key and check KeyManager is CUSTOMER before
proceeding, as a safety check against accidentally pointing key_id at an AWS-managed
key.RotationPeriodInDays and NextRotationDate, also returned by
get-key-rotation-status, to alert when a key's rotation period itself was changed to
something unexpectedly long, not only when rotation is fully disabled.aws/s3, aws/rds, and similar) rotate automatically on their own
schedule and cannot have rotation disabled or enabled through this API the same way,
pointing key_id at one is out of scope for this gate, scope it to customer-managed
keys only.enable-key-rotation on a key pending deletion, or in certain other non-Enabled key
states, fails. allowFailure: true on enable_rotation_* lets the flow continue to
verify_rotation_* either way, which then correctly reports the fix did not take.key_id, there is
no bulk-enable path, intentionally, enabling rotation account-wide without reviewing
which keys are AWS-managed first is not something this flow will do automatically.KeyRotationEnabled only covers automatic rotation. RotateKeyOnDemand, a separate
operation, performs a one-time rotation without changing this setting, this gate does
not check for or trigger that.