Skip to main content

cdk-preflight

cdk-preflight

Catch deploy-time CloudFormation failures at cdk synth time.

monthly real-deploy verification npm version npm total downloads 1844 bundled rules

Some CloudFormation constraints are not expressed in resource provider schemas — they live only in documentation, in service API validation, or across multiple properties. Templates that violate them pass cdk synth, pass CloudFormation pre-deployment validation, and then fail minutes into a deployment, burning a rollback cycle.

cdk-preflight is a curated Rego rule pack for exactly those constraints, evaluated with the CloudFormation validation engine that ships inside aws-cdk-lib (>= 2.267.0). By default a violation fails cdk synth — a template that is known to fail at deploy time never leaves your machine.

The pack aims at every deploy-time failure that no existing CDK mechanism already catches — nothing narrower. Every bundled rule is backed by a fail/pass template pair, and the failure has been reproduced against real AWS. The handful of rules that could not be reproduced are marked doc-only and report as warnings: they show up in the validation report but never fail synth. Rules that the built-in validation engine already covers are deliberately not duplicated — a test suite enforces this.

Requires aws-cdk-lib >= 2.267.0 (released 2026-08-27) — the first release that bundles the CloudFormation validation engine. On older versions the rules cannot run at all.

Quick start

npm i -D cdk-preflight
npx cdkpf init   # inserts Preflight.apply(app) into your CDK app
                         # (`npx cdkpf init` is the same command, shorter)

or add one line yourself:

import { Preflight } from 'cdk-preflight';

const app = new App();
Preflight.apply(app);

On violation, cdk synth fails with one error per finding, including the construct trace:

ERROR idle_timeout.timeout_seconds is 5000 but must be between 1 and 4000 seconds (cdk-preflight)
   MyStack/Alb/Resource (Alb16C2F182) aws-cdk-lib.aws_elasticloadbalancingv2.CfnLoadBalancer

Synthesis finished with errors

What it catches

Four ordinary-looking snippets. All of them pass cdk synth and CloudFormation pre-deployment validation, and all of them fail minutes into a deployment:

// 1) pf-iam-inline-policy-size  enumerate buckets, grant each one, blow past 10,240 chars
//    "Maximum policy size of 10240 bytes exceeded for role IngestRole"
//    (via role.addToPolicy the CDK auto-splits into managed policies instead,
//     and you hit the 6,144-char limit as pf-iam-managed-policy-size)
new iam.Policy(this, 'IngestPolicy', {
  roles: [role],
  statements: [new iam.PolicyStatement({
    actions: ['s3:GetObject', 's3:ListBucket'],
    resources: Array.from({ length: 200 },
      (_, i) => `arn:aws:s3:::data-lake-landing-zone-${i}/year=*/month=*/*`),
  })],
});

// 2) pf-lambda-env-size  a config blob in the environment, over the 4KB total
//    "Lambda was unable to configure your environment variables because the
//     environment variables you have provided exceeded the 4KB limit"
new lambda.Function(this, 'Fn', {
  runtime: lambda.Runtime.NODEJS_22_X,
  handler: 'index.handler',
  code: lambda.Code.fromInline('exports.handler = async () => {};'),
  environment: { FEATURE_FLAGS: JSON.stringify(bigFeatureFlagMap) },
});

// 3) pf-sfn-asl-missing-state (+ pf-sfn-asl-unreachable-state)  a typo in a state name
//    "Invalid State Machine Definition: 'MISSING_TRANSITION_TARGET: ...'"
new sfn.StateMachine(this, 'Pipeline', {
  definitionBody: sfn.DefinitionBody.fromString(JSON.stringify({
    StartAt: 'Validate',
    States: {
      Validate: { Type: 'Pass', Next: 'Transform' },
      Trasform: { Type: 'Pass', End: true },   // typo: Transform
    },
  })),
});

// 4) pf-logs-filter-pattern-bracket  a filter pattern opened with '[' and never closed
//    "If a filter pattern starts with '[' it must end with ']'"
new logs.MetricFilter(this, 'ErrorFilter', {
  logGroup,
  metricNamespace: 'Pipeline',
  metricName: 'Errors',
  filterPattern: logs.FilterPattern.literal('[time, level=ERROR, msg'),
});

None of these are type errors, so the L2 constructs accept them; none of them are expressible in a resource schema, so CloudFormation accepts the template. With Preflight.apply(app) in place they fail cdk synth instead.

Observe-only mode

To roll the rules out gradually, start with enforce: false: findings then surface as synth warnings through the CDK built-in validator, with construct traces and per-finding acknowledgement:

Preflight.apply(app, { enforce: false });
WARNING idle_timeout.timeout_seconds is 5000 but must be between 1 and 4000 seconds (CloudFormation Validate)
   MyStack/Alb (Alb) aws-cdk-lib.aws_elasticloadbalancingv2.CfnLoadBalancer
   Acknowledge with 'CloudFormation-Validate::pf-elbv2-lb-idle-timeout-range'

Known limitation with stages. The AWS CDK CLI drops validation findings for stacks nested in a Stage before printing them, so in observe-only mode those findings appear only in cdk.out/validation-report.json and never on the console. Enforce mode is not affected: cdk-preflight reports such findings itself and fails synthesis. This is a CLI-side bug (present since aws-cdk 2.1128.1), not a rule evaluation problem.

If the rules cannot run, the build stops. When the evaluation engine fails on a template (a rule pack that does not compile, an engine bug), enforce mode reports it as a violation named pf-engine-error and fails synthesis for that stack instead of passing green with no rule having run. The other stacks keep their rules. pf-engine-error is not a bundled rule and cannot be excluded; enforce: false unblocks the build if you need one.

Option Default Effect
enforce true Violations of bundled rules fail synthesis; set to false to only warn
strict false With enforce: also fail on error-class findings (ERROR/FATAL, e.g. F3034) of the built-in validation engine itself, which the CDK currently downgrades to warnings
exclude [] Rule ids to disable
includeUpstreamPending true Include rules already proposed to the upstream engine but not yet merged

To opt out of a single rule everywhere, pass its id in exclude. To suppress a single finding on one construct, acknowledge it — this works in both modes, the id prefix just differs (cdk-preflight:: when enforcing, CloudFormation-Validate:: in observe-only, as printed in the warning text):

cdk.Validations.of(errorFilter).acknowledge({
  id: 'cdk-preflight::pf-logs-filter-pattern-bracket',
  reason: 'log group is written by a legacy producer; pattern is fixed upstream',
});

Bundled rules

See docs/rules.md for the generated rule table. Highlights:

  • ELBv2: idle_timeout / deregistration_delay / slow_start attribute ranges (stringly-typed Key/Value attributes are invisible to schema validation)
  • IAM: managed (6,144 chars) and inline (role/group/user) policy document size limits
  • CloudFront: MinTTL <= DefaultTTL <= MaxTTL ordering, ACM certificates must live in us-east-1
  • Step Functions: Next/Default/Choices must reference defined states (a dangling StartAt is already caught by the engine's built-in E3601)
  • EC2: security group TCP/UDP port ranges and FromPort <= ToPort

For AI agents

To add cdk-preflight to a CDK app:

  1. npm i -D cdk-preflight
  2. npx cdk-preflight init — or the shorter alias npx cdkpf init (finds the entry point via cdk.json and inserts Preflight.apply(app); use --dry-run to preview)
  3. Run cdk synth. Violations fail the synth with a validation report; each finding carries a rule id, the actual value, and a suggested fix
  4. To fix a finding, prefer correcting the resource. To opt out instead: exclude: ['<rule-id>'] disables one rule, enforce: false switches to warn-only reporting

The machine-readable findings are written to cdk.out/validation-report.json when synthesizing with -c @aws-cdk/core:validationReportJson=true.

Report missed deploy errors

cdk-preflight only knows the failure modes it has benched. When a deploy fails on a constraint the pack should have caught, that report is how new rules get added — but at that moment cdk-preflight is out of the loop (it passed the synth), so the instruction has to already sit in your agent's context. Paste this into your repository's AGENTS.md (or CLAUDE.md):

## cdk-preflight: report missed deploy errors

If `cdk deploy` fails even though `cdk synth` (with cdk-preflight enabled) passed,
and the failure is a service-side validation rejection — CREATE_FAILED/UPDATE_FAILED
with a 400-class message such as "Invalid request provided: ..." — cdk-preflight is
missing a rule. Report it:

1. Qualify first. NOT reportable: AccessDenied / permissions, service quotas,
   throttling, name conflicts with existing resources, stabilization timeouts.
2. Redact account-specific data (account IDs -> 123456789012, real ARNs/domains
   -> placeholders).
3. Search existing issues: https://github.com/badmintoncryer/cdk-preflight/issues
4. With your user's approval, open a "Missed deploy error" issue with the verbatim
   error message, the resource type, a minimal template snippet, and your
   aws-cdk-lib / cdk-preflight versions.

Scope and rule lifecycle

A constraint belongs in the pack when violating it makes a real deployment fail and no layer that sees the same synthesized template already blocks it. There is no further "is this worth a rule" question — if the gap is real, it gets a rule.

CDK L2 construct validation is deliberately not one of those layers. CfnXxx usage, escape hatches, addPropertyOverride, cloudformation-include and migrated templates all bypass L2, so an L2 guard covering the same mistake neither disqualifies a rule nor retires one.

That makes growth the normal state, and it has a consequence worth knowing before you upgrade: new rules land in minor releases, so a minor upgrade can newly fail a cdk synth that passed yesterday. That is intended, not a regression. If you need a frozen rule set, pin the version; to drop a single rule, exclude: ['<rule-id>']; to see everything without failing the build, enforce: false.

Rules move the other way too. Once the validation engine bundled in aws-cdk-lib (or CloudFormation's own pre-deploy validation) starts blocking a constraint, the rule is deleted rather than kept as a duplicate — staying on an older aws-cdk-lib and an older cdk-preflight keeps the old behavior.

How it works

Preflight.apply() evaluates the rules with the cloudformation-validate Rust/WASM engine that ships inside aws-cdk-lib — no extra binaries, no network access at synth time. In the default enforce mode the engine is invoked through a dedicated CDK validation plugin so that violations fail synthesis; with enforce: false the rules are instead injected into the CDK built-in CloudFormationValidatePlugin and reported as warnings.

Constraints that can be expressed in schemas or generic engine rules also make good upstream PRs to that engine, but nothing here waits on one — the upstream release cycle is deliberately slower than this pack's. Each rule's meta.yaml tracks its upstream status so that retirement stays bookkeeping.

Requirements

  • aws-cdk-lib >= 2.267.0, released 2026-08-27 (the first release that bundles the built-in CloudFormation validator). This is a recent release — an existing CDK app may need an upgrade before cdk-preflight can run.

Contributing

Rule authoring, the verification gates (including real-deploy reproduction), and the test layout are documented in AGENTS.md — written for AI coding agents and humans alike.

License

Apache-2.0

Release files for cdk-preflight 0.0.88

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cdk-preflight 0.0.88
File Size Uploaded
cdk_preflight-0.0.88.tar.gz 1.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for cdk-preflight 0.0.88
File Interpreter ABI Platform
cdk_preflight-0.0.88-py3-none-any.whl Python 3 none any Details

Total release size: 3.0 MB

Release files / cdk_preflight-0.0.88.tar.gz

Download URL cdk_preflight-0.0.88.tar.gz
Size 1.5 MB
Tags Source
SHA-256 checksum
How to use checksums
c9be66cb79903f8434cf7539c7396f8d6e1024f3a6d716632a1d3a86c2c3979c
BLAKE2b-256 checksum
How to use checksums
92fc9add7d69c8c7790a57054c198e2aa4d9f3017a4d2ee8a04d3c34c77f2e88
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.14.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / cdk_preflight-0.0.88-py3-none-any.whl

Download URL cdk_preflight-0.0.88-py3-none-any.whl
Size 1.5 MB
Tags Python 3
SHA-256 checksum
How to use checksums
d8d34491dd4d4cb604981e1be6932c121b321f5da3517764518ce46a377701ea
BLAKE2b-256 checksum
How to use checksums
d8eef66466fbacecf30d2ab6477d811100b4bb14e89516098da675cf1ee4c555
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.14.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release history Release notifications | RSS feed

0.0.99

2 release files

0.0.98

2 release files

0.0.97

2 release files

0.0.96

2 release files

0.0.95

2 release files

0.0.94

2 release files

0.0.93

2 release files

0.0.92

2 release files

0.0.91

2 release files

0.0.90

2 release files

0.0.89

2 release files

This release

0.0.88 This release

2 release files

0.0.87

2 release files

0.0.86

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page