Run
cdk diffand get a beautifully formatted cost-aware summary posted to your Bitbucket, GitHub, or GitLab PR/MR — automatically.
- Runs
cdk diffand streams the raw output to your pipeline console - Parses the diff into a structured summary (added/modified/removed resources)
- Estimates monthly cost impact for each resource (live AWS Pricing API + static fallback)
- Posts a formatted Markdown comment to your PR/MR
- On subsequent runs, updates the same comment instead of creating duplicates
https://www.npmjs.com/package/cdk-diff-report
The fastest way to get started on GitHub:
# .github/workflows/cdk-diff.yml
name: CDK Diff
on: pull_request
jobs:
cdk-diff:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- run: npm ci # install your CDK app deps
- uses: dzon337/cdk-diff-visualizer@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-central-1That's it — every PR gets a cost-aware diff comment automatically.
| Input | Description | Default |
|---|---|---|
cdk-args |
Arguments forwarded to cdk diff (comma-separated) |
--all |
html-output |
Path to write an HTML report | — |
dry-run |
Preview markdown without posting | false |
working-directory |
Directory containing cdk.json |
. |
node-version |
Node.js version to use | 20 |
npm install -g cdk-diff-reportCreate a .cdkdiffreportrc file in the root of your CDK project (next to cdk.json):
{
"platform": "github",
"cdkArgs": ["--all"],
"htmlOutput": "cdk-diff.html"
}# In your CDK project directory (where cdk.json lives):
cdk-diff-report # run diff + post PR comment
cdk-diff-report --dry-run # run diff + print markdown preview (no posting)
cdk-diff-report --help # show all optionsPlace the file in the root of the project where you run cdk diff — the same
directory as your cdk.json. The tool looks for .cdkdiffreportrc or
.cdkdiffreportrc.json in the current working directory.
my-cdk-project/
├── cdk.json
├── .cdkdiffreportrc ← put it here
├── lib/
│ └── my-stack.ts
├── bin/
│ └── app.ts
└── package.json
Note: JSON does not support comments. The
//comments above are for illustration only. Your actual.cdkdiffreportrcmust be valid JSON without comments.
| Field | RC file key | Env var override | Default |
|---|---|---|---|
| Platform | platform |
CDK_DIFF_PLATFORM |
"bitbucket" |
| CDK args | cdkArgs |
CDK_DIFF_CDK_ARGS (comma-separated) |
["--all"] |
| HTML report | htmlOutput |
CDK_DIFF_HTML_OUTPUT |
— |
| Dry run | dryRun |
CDK_DIFF_DRY_RUN (true/false) |
false |
| BB API URL | bitbucketApiUrl |
CDK_DIFF_BITBUCKET_API_URL |
https://api.bitbucket.org/2.0 |
| GL API URL | gitlabApiUrl |
CDK_DIFF_GITLAB_API_URL |
auto-detected |
| BB workspace | workspace |
CDK_DIFF_WORKSPACE |
$BITBUCKET_WORKSPACE |
| BB repo slug | repoSlug |
CDK_DIFF_REPO_SLUG |
$BITBUCKET_REPO_SLUG |
Priority: CDK_DIFF_* env vars → .cdkdiffreportrc file → built-in defaults.
If your buildspec lives in a different repo than your CDK app, you have two options:
# buildspec.yml (in your CI/CD repo)
phases:
build:
commands:
- git clone https://bitbucket.org/my-team/my-cdk-app.git /tmp/cdk-app
- cd /tmp/cdk-app && npm ci
- cdk-diff-report --cwd /tmp/cdk-appThe tool reads .cdkdiffreportrc and runs cdk diff from the --cwd directory.
Set everything via CDK_DIFF_* env vars in your buildspec — no need to put a
.cdkdiffreportrc in the CDK repo at all:
# buildspec.yml
env:
variables:
CDK_DIFF_PLATFORM: "bitbucket"
CDK_DIFF_CDK_ARGS: "--all"
CDK_DIFF_HTML_OUTPUT: "cdk-diff.html"
BITBUCKET_WORKSPACE: "my-team"
BITBUCKET_REPO_SLUG: "my-cdk-app"
secrets-manager:
BITBUCKET_ACCESS_TOKEN: "bitbucket/access-token"
phases:
build:
commands:
- export BITBUCKET_PR_ID=$(echo $CODEBUILD_WEBHOOK_TRIGGER | grep -oP '\d+')
- cd /path/to/cdk-app && npm ci
- cdk-diff-reportThe tool estimates the monthly cost impact of your infrastructure changes using a three-tier strategy:
- CloudFormation template analysis — reads actual resource properties from
cdk.out/(instance types, memory sizes, DB engines, etc.) - AWS Pricing API — queries real on-demand prices for EC2, RDS, ElastiCache, NAT Gateway, Lambda, and ECS Fargate
- Static fallback — uses a built-in table of ~80 resource types when the API is unavailable
The cost column appears in both the PR comment and the HTML report:
| ✅ Added | `MyBucket` | S3 › Bucket | +$0.023/mo |
| ✅ Added | `MyFunction` | Lambda › Func | +$0.620/mo |
| ❌ Removed | `OldDb` | RDS › DBInstance | -$49.64/mo |
Cost estimates require AWS credentials (the same ones used for
cdk diff). If credentials are unavailable, the tool falls back to static estimates silently.
# .github/workflows/cdk-diff.yml
name: CDK Diff
on: pull_request
jobs:
cdk-diff:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- run: npm ci
- uses: dzon337/cdk-diff-visualizer@v1
with:
cdk-args: '--all'
html-output: 'cdk-diff.html'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-central-1steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx cdk-diff-report
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-central-1Environment variables (set automatically by GitHub Actions):
| Variable | Source |
|---|---|
GITHUB_TOKEN |
Auto-provided, needs pull-requests: write permission |
GITHUB_REF |
Auto-set, PR number extracted from refs/pull/N/merge |
GITHUB_REPOSITORY |
Auto-set (owner/repo) |
# .gitlab-ci.yml
cdk-diff:
image: node:20
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- npm ci
- npx cdk-diff-report
variables:
GITLAB_TOKEN: $CDK_DIFF_GITLAB_TOKENAdd CDK_DIFF_GITLAB_TOKEN in Settings → CI/CD → Variables (masked, protected).
Important:
CI_MERGE_REQUEST_IIDis only set in merge request pipelines. Make sure you userules: - if: $CI_PIPELINE_SOURCE == "merge_request_event".
| Variable | Source |
|---|---|
GITLAB_TOKEN |
You create this — project token with api scope |
CI_PROJECT_ID |
Auto-set |
CI_MERGE_REQUEST_IID |
Auto-set (MR pipelines only) |
CI_API_V4_URL |
Auto-set |
# bitbucket-pipelines.yml
pipelines:
pull-requests:
'**':
- step:
name: CDK Diff
script:
- npm ci
- npx cdk-diff-reportAdd BITBUCKET_ACCESS_TOKEN in Repository settings → Repository variables.
| Variable | Source |
|---|---|
BITBUCKET_ACCESS_TOKEN |
You create this — needs pullrequest:write scope |
BITBUCKET_PR_ID |
Auto-set |
BITBUCKET_WORKSPACE |
Auto-set |
BITBUCKET_REPO_SLUG |
Auto-set |
When your CDK project uses AWS CodePipeline triggered by Bitbucket PRs, CodeBuild doesn't set the Bitbucket env vars automatically. You need to provide them yourself.
aws secretsmanager create-secret \
--name bitbucket/access-token \
--secret-string "YOUR_BITBUCKET_APP_PASSWORD_OR_TOKEN"Create a Bitbucket App Password at https://bitbucket.org/account/settings/app-passwords/ with the
pullrequest:writescope. Or use a repository access token.
When CodeBuild is triggered by a PR webhook, the env var CODEBUILD_WEBHOOK_TRIGGER
is set to pr/123 (where 123 is the PR number). Extract it in your buildspec:
# buildspec.yml
version: 0.2
env:
secrets-manager:
BITBUCKET_ACCESS_TOKEN: "bitbucket/access-token"
variables:
# Set these to match your Bitbucket repository
BITBUCKET_WORKSPACE: "my-team"
BITBUCKET_REPO_SLUG: "my-cdk-app"
phases:
install:
runtime-versions:
nodejs: 20
commands:
- npm ci
- npm install -g aws-cdk cdk-diff-report
build:
commands:
# Extract PR ID from CodeBuild webhook trigger (format: "pr/123")
- export BITBUCKET_PR_ID=$(echo $CODEBUILD_WEBHOOK_TRIGGER | grep -oP '\d+')
- echo "PR ID = $BITBUCKET_PR_ID"
# Run the diff + post comment to Bitbucket
- cdk-diff-report
artifacts:
files:
- cdk-diff.html
discard-paths: yesIn your CodeBuild project:
- Source: Connect to Bitbucket via CodeStar Connections
- Webhook: Enable webhook events for
PULL_REQUEST_CREATEDandPULL_REQUEST_UPDATED - IAM Role: Grant the CodeBuild role permission to read from Secrets Manager:
{ "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:*:*:secret:bitbucket/access-token-*" } - Environment variables: Set
BITBUCKET_WORKSPACEandBITBUCKET_REPO_SLUGeither in the buildspec (above) or in the CodeBuild project configuration.
Bitbucket PR created/updated
→ CodeBuild webhook trigger: CODEBUILD_WEBHOOK_TRIGGER=pr/123
→ buildspec extracts PR ID: BITBUCKET_PR_ID=123
→ cdk-diff-report runs cdk diff
→ posts formatted comment to Bitbucket PR #123
Tip: If you're using CodePipeline (not direct CodeBuild webhook), the PR ID is not available via
CODEBUILD_WEBHOOK_TRIGGER. In that case, pass it as a CodePipeline variable or use a Lambda step to resolve it from the source revision.
The tool posts a collapsible Markdown comment with per-stack breakdown:
## 🚀 CDK Diff Report
> 📈 **Estimated monthly cost impact: +$1.04/mo**
| ✅ Added | ⚠️ Modified | ❌ Removed | 🔐 IAM stacks | 💰 Est. cost |
|---------|------------|-----------|--------------|-------------|
| 5 | 0 | 0 | 1 | +$1.04/mo |
<details>
<summary><strong>MyStack</strong> — +5 · 🔐 IAM · 💰 +$1.04/mo</summary>
| Change | Logical ID | Type | Est. Cost |
|-----------|-------------|----------------|--------------|
| ✅ Added | `MyBucket` | S3 › Bucket | +$0.023/mo |
| ✅ Added | `MyFunction`| Lambda › Func | +$0.620/mo |
| ✅ Added | `MyQueue` | SQS › Queue | +$0.400/mo |
</details>
On the next run, the same comment is updated with the latest diff.
import { run, parseCdkDiff, generateMarkdownComment, enrichWithLivePricing } from 'cdk-diff-report';
// Full run (diff + comment)
await run({ dryRun: true });
// Just the parser
const diff = parseCdkDiff(rawCdkOutput);
console.log(diff.totalAdded, diff.costImpact.netCost);
// Platform-specific comment management
import { resolveGitHubEnv, upsertGitHubPrComment } from 'cdk-diff-report';
const env = resolveGitHubEnv();
await upsertGitHubPrComment(env, '## My Report\n...');Q: Where does .cdkdiffreportrc go?
A: In the root of your CDK project — the same directory where cdk.json lives.
Q: Do I need AWS credentials for cost estimation?
A: The tool uses the same AWS credentials you already provide for cdk diff.
If credentials are missing, cost estimates fall back to static defaults.
Q: What if I don't have a PR (e.g. push to main)?
A: The tool runs cdk diff and prints the summary to the console, but skips
the PR comment gracefully — no crash.
Q: Can I diff only specific stacks?
A: Yes, set "cdkArgs": ["MyStack", "OtherStack"] in your config.
Q: Does it support monorepos?
A: Yes, run cdk-diff-report from the directory containing cdk.json.
Each CDK app needs its own .cdkdiffreportrc.
MIT
{ // Required: which CI platform are you using? // Options: "bitbucket" | "github" | "gitlab" "platform": "github", // Arguments forwarded to `cdk diff` // Default: ["--all"] // Examples: // ["--all"] → diff all stacks // ["MyStack"] → diff only MyStack // ["--all", "--no-change-set"] → skip change set creation (faster) "cdkArgs": ["--all"], // Optional: write a standalone HTML report to this file path // Great for CI artifacts — gives you a visual dashboard "htmlOutput": "cdk-diff.html", // Optional: never post to PR, just preview in the terminal // Default: false "dryRun": false, // Bitbucket Server only — override the API base URL // Default: "https://api.bitbucket.org/2.0" "bitbucketApiUrl": "https://api.bitbucket.org/2.0", // Self-managed GitLab only — override the API URL // Default: auto-detected from $CI_API_V4_URL or "https://gitlab.com/api/v4" "gitlabApiUrl": "https://gitlab.mycompany.com/api/v4" }