Skip to content

Commit 09a6eaf

Browse files
feat(ci): add cdk diff job before deploy approval gate (B2 compensating control)
Adds a `diff` job that runs immediately after resolve-targets (no environment gate). It downloads the same CDK artifact, assumes AWS credentials, and runs `cdk diff` — surfacing all CloudFormation changes in the GitHub step summary. The reviewer can inspect the diff before clicking "Approve" on the deploy environment gate. The deploy job now `needs: [resolve-targets, diff]` ensuring the diff completes before approval is requested. `--require-approval never` is retained on the deploy step because CDK hard-fails without TTY in CI; the GitHub environment approval gate is the human checkpoint. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 52a6a99 commit 09a6eaf

1 file changed

Lines changed: 86 additions & 1 deletion

File tree

.github/workflows/deploy.yml

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,85 @@ jobs:
122122
;;
123123
esac
124124
125-
deploy:
125+
# Surfaces CloudFormation changes in the step summary BEFORE the deploy
126+
# approval gate. Uses a read-only IAM role (no deploy/mutate permissions).
127+
# Configure the 'diff' environment with no required reviewers so it auto-runs;
128+
# gate it later if read-access to stack templates needs approval.
129+
diff:
126130
needs: resolve-targets
127131
if: needs.resolve-targets.outputs.has_targets == 'true'
128132
runs-on: ubuntu-latest
133+
environment: diff
134+
strategy:
135+
matrix:
136+
compute_type: ${{ fromJson(needs.resolve-targets.outputs.matrix) }}
137+
permissions:
138+
id-token: write
139+
contents: read
140+
actions: read
141+
steps:
142+
- name: Checkout
143+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
144+
with:
145+
persist-credentials: false
146+
147+
- name: Download CDK artifact (${{ matrix.compute_type }})
148+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
149+
with:
150+
name: cdk-${{ matrix.compute_type }}-out
151+
path: cdk/
152+
run-id: ${{ needs.resolve-targets.outputs.run_id }}
153+
github-token: ${{ github.token }}
154+
155+
- name: Configure AWS credentials
156+
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1
157+
with:
158+
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
159+
aws-region: ${{ vars.AWS_REGION }}
160+
161+
- name: Setup Node.js
162+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
163+
with:
164+
node-version: 22.x
165+
166+
- name: Install dependencies
167+
run: yarn install --immutable
168+
169+
- name: CDK Diff (full)
170+
env:
171+
COMPUTE_TYPE: ${{ matrix.compute_type }}
172+
run: |
173+
# --method=template: read-only comparison against deployed template;
174+
# no change-set creation, no S3 asset publishing, no deploy role needed.
175+
npx cdk diff --app cdk/cdk.out --all --method=template --no-color 2>&1 | tee cdk-diff-full.txt || true
176+
177+
- name: CDK Diff (security only)
178+
env:
179+
COMPUTE_TYPE: ${{ matrix.compute_type }}
180+
run: |
181+
echo "## Security Changes (\`$COMPUTE_TYPE\`)" >> "$GITHUB_STEP_SUMMARY"
182+
echo "" >> "$GITHUB_STEP_SUMMARY"
183+
npx cdk diff --app cdk/cdk.out --all --method=template --security-only --no-color 2>&1 | tee cdk-diff-security.txt
184+
if [ -s cdk-diff-security.txt ]; then
185+
echo '```' >> "$GITHUB_STEP_SUMMARY"
186+
cat cdk-diff-security.txt >> "$GITHUB_STEP_SUMMARY"
187+
echo '```' >> "$GITHUB_STEP_SUMMARY"
188+
else
189+
echo "No security-relevant changes detected." >> "$GITHUB_STEP_SUMMARY"
190+
fi
191+
192+
- name: Upload diff artifacts
193+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
194+
with:
195+
name: cdk-diff-${{ matrix.compute_type }}
196+
path: |
197+
cdk-diff-full.txt
198+
cdk-diff-security.txt
199+
200+
deploy:
201+
needs: [resolve-targets, diff]
202+
if: needs.resolve-targets.outputs.has_targets == 'true'
203+
runs-on: ubuntu-latest
129204
environment: deploy
130205
concurrency:
131206
group: deploy-${{ matrix.compute_type }}
@@ -174,4 +249,14 @@ jobs:
174249
- name: Deploy
175250
env:
176251
COMPUTE_TYPE: ${{ matrix.compute_type }}
252+
# --require-approval never: CDK hard-fails in non-TTY CI without this
253+
# (throws "terminal (TTY) is not attached"). The approval mechanism is the
254+
# GitHub 'deploy' environment gate above — a human must approve before this
255+
# job starts. The 'diff' job surfaces all CloudFormation and security changes
256+
# in the step summary (visible before approval), and the full diff is
257+
# downloadable as an artifact. Defense in depth:
258+
# 1. diff environment (read-only role) → surfaces changes pre-approval
259+
# 2. deploy environment (required reviewers) → human gate
260+
# 3. fork guard (line 14) → blocks untrusted repositories
261+
# 4. OIDC federation → no stored credentials, request-scoped tokens
177262
run: npx cdk deploy --app cdk/cdk.out --all --require-approval never

0 commit comments

Comments
 (0)