Skip to content

Commit db7ee0a

Browse files
committed
Add Slack notifications with resource counts and error reporting
- Capture nuke output to parse deleted/error counts - Upload logs as artifacts for aggregation - Notify jobs download artifacts and aggregate counts across all regions - Slack message includes: status, deleted count, error count (if any), run link - Fetch webhook URL from AWS Secrets Manager (PhxDevOps account)
1 parent ee5200d commit db7ee0a

File tree

1 file changed

+184
-20
lines changed

1 file changed

+184
-20
lines changed

.github/workflows/nuke.yml

Lines changed: 184 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ permissions:
2424
contents: read
2525

2626
env:
27-
GO_VERSION: '1.21'
2827
MISE_VERSION: '2025.12.10'
2928
COMMON_EXCLUDES: >-
3029
--exclude-resource-type iam
@@ -40,13 +39,6 @@ env:
4039
--exclude-resource-type config-rules
4140
--exclude-resource-type nat-gateway
4241
--exclude-resource-type ec2-subnet
43-
ALL_REGIONS: >-
44-
ap-northeast-1 ap-northeast-2 ap-northeast-3
45-
ap-south-1 ap-southeast-1 ap-southeast-2
46-
ca-central-1 eu-central-1 eu-north-1
47-
eu-west-1 eu-west-2 eu-west-3
48-
me-central-1 sa-east-1
49-
us-east-1 us-east-2 us-west-1 us-west-2
5042
5143
jobs:
5244
# ============================================
@@ -56,6 +48,9 @@ jobs:
5648
name: "PhxDevOps: Global"
5749
runs-on: ubuntu-latest
5850
timeout-minutes: 30
51+
outputs:
52+
deleted_count: ${{ steps.nuke.outputs.deleted_count }}
53+
error_count: ${{ steps.nuke.outputs.error_count }}
5954
if: |
6055
github.event_name == 'push' ||
6156
github.event_name == 'workflow_dispatch' && (inputs.account == 'phxdevops' || inputs.account == '') ||
@@ -81,16 +76,30 @@ jobs:
8176
restore-keys: ${{ runner.os }}-go-
8277
- run: go mod download
8378
- name: Nuke global resources
79+
id: nuke
8480
run: |
81+
set +e
8582
go run -ldflags="-X 'main.VERSION=${{ github.sha }}'" main.go aws \
8683
--older-than 2h --force --config ./.github/nuke_config.yml \
8784
--region global ${{ env.COMMON_EXCLUDES }} \
88-
--delete-unaliased-kms-keys --log-level info
85+
--delete-unaliased-kms-keys --log-level info 2>&1 | tee /tmp/nuke.log
86+
EXIT_CODE=${PIPESTATUS[0]}
87+
88+
# Parse output for counts
89+
DELETED=$(grep -c "^\s*INFO\s*\[Deleted\]" /tmp/nuke.log || echo "0")
90+
ERRORS=$(grep -c "^\s*ERROR\s*\[Failed\]" /tmp/nuke.log || echo "0")
91+
echo "deleted_count=${DELETED}" >> $GITHUB_OUTPUT
92+
echo "error_count=${ERRORS}" >> $GITHUB_OUTPUT
93+
94+
exit $EXIT_CODE
8995
9096
phxdevops_regional:
9197
name: "PhxDevOps: ${{ matrix.region }}"
9298
runs-on: ubuntu-latest
9399
timeout-minutes: 20
100+
outputs:
101+
deleted_count: ${{ steps.nuke.outputs.deleted_count }}
102+
error_count: ${{ steps.nuke.outputs.error_count }}
94103
if: |
95104
github.event_name == 'push' ||
96105
github.event_name == 'workflow_dispatch' && (inputs.account == 'phxdevops' || inputs.account == '') ||
@@ -120,11 +129,28 @@ jobs:
120129
restore-keys: ${{ runner.os }}-go-
121130
- run: go mod download
122131
- name: Nuke ${{ matrix.region }}
132+
id: nuke
123133
run: |
134+
set +e
124135
go run -ldflags="-X 'main.VERSION=${{ github.sha }}'" main.go aws \
125136
--older-than 2h --force --config ./.github/nuke_config.yml \
126137
--region ${{ matrix.region }} ${{ env.COMMON_EXCLUDES }} \
127-
--delete-unaliased-kms-keys --log-level info
138+
--delete-unaliased-kms-keys --log-level info 2>&1 | tee /tmp/nuke.log
139+
EXIT_CODE=${PIPESTATUS[0]}
140+
141+
DELETED=$(grep -c "^\s*INFO\s*\[Deleted\]" /tmp/nuke.log || echo "0")
142+
ERRORS=$(grep -c "^\s*ERROR\s*\[Failed\]" /tmp/nuke.log || echo "0")
143+
echo "deleted_count=${DELETED}" >> $GITHUB_OUTPUT
144+
echo "error_count=${ERRORS}" >> $GITHUB_OUTPUT
145+
146+
exit $EXIT_CODE
147+
- name: Upload results
148+
if: always()
149+
uses: actions/upload-artifact@v4
150+
with:
151+
name: phxdevops-${{ matrix.region }}
152+
path: /tmp/nuke.log
153+
retention-days: 7
128154

129155
phxdevops_notify:
130156
name: "PhxDevOps: Notify"
@@ -141,12 +167,32 @@ jobs:
141167
with:
142168
role-to-assume: arn:aws:iam::087285199408:role/cloud-nuke-gha
143169
aws-region: us-east-1
144-
- name: Send Slack notification
170+
- name: Download all artifacts
171+
uses: actions/download-artifact@v4
172+
with:
173+
pattern: phxdevops-*
174+
path: /tmp/logs
175+
merge-multiple: true
176+
continue-on-error: true
177+
- name: Aggregate and notify
145178
run: |
146179
WEBHOOK_URL=$(aws secretsmanager get-secret-value \
147180
--secret-id cloud-nuke/slack-webhook \
148181
--query SecretString --output text)
149182
183+
# Aggregate counts from all logs
184+
TOTAL_DELETED=0
185+
TOTAL_ERRORS=0
186+
if [ -d /tmp/logs ]; then
187+
TOTAL_DELETED=$(grep -rh "^\s*INFO\s*\[Deleted\]" /tmp/logs 2>/dev/null | wc -l || echo "0")
188+
TOTAL_ERRORS=$(grep -rh "^\s*ERROR\s*\[Failed\]" /tmp/logs 2>/dev/null | wc -l || echo "0")
189+
fi
190+
# Add global counts
191+
GLOBAL_DELETED="${{ needs.phxdevops_global.outputs.deleted_count }}"
192+
GLOBAL_ERRORS="${{ needs.phxdevops_global.outputs.error_count }}"
193+
TOTAL_DELETED=$((TOTAL_DELETED + ${GLOBAL_DELETED:-0}))
194+
TOTAL_ERRORS=$((TOTAL_ERRORS + ${GLOBAL_ERRORS:-0}))
195+
150196
if [ "${{ needs.phxdevops_global.result }}" == "success" ] && \
151197
[ "${{ needs.phxdevops_regional.result }}" == "success" ]; then
152198
STATUS="✅ Success"
@@ -156,6 +202,14 @@ jobs:
156202
COLOR="danger"
157203
fi
158204
205+
# Build message
206+
MSG="*PhxDevOps Nuke*: ${STATUS}"
207+
MSG="${MSG}\nDeleted: ${TOTAL_DELETED} resources"
208+
if [ "$TOTAL_ERRORS" -gt 0 ]; then
209+
MSG="${MSG} | Errors: ${TOTAL_ERRORS}"
210+
fi
211+
MSG="${MSG}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
212+
159213
curl -sS -X POST "$WEBHOOK_URL" -H "Content-Type: application/json" -d @- <<EOF
160214
{
161215
"attachments": [{
@@ -164,7 +218,7 @@ jobs:
164218
"type": "section",
165219
"text": {
166220
"type": "mrkdwn",
167-
"text": "*PhxDevOps Nuke*: ${STATUS}\nGlobal: ${{ needs.phxdevops_global.result }} | Regional: ${{ needs.phxdevops_regional.result }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
221+
"text": "${MSG}"
168222
}
169223
}]
170224
}]
@@ -178,6 +232,9 @@ jobs:
178232
name: "ConfigTests: Global"
179233
runs-on: ubuntu-latest
180234
timeout-minutes: 30
235+
outputs:
236+
deleted_count: ${{ steps.nuke.outputs.deleted_count }}
237+
error_count: ${{ steps.nuke.outputs.error_count }}
181238
if: |
182239
github.event_name == 'workflow_dispatch' && (inputs.account == 'configtests' || inputs.account == '') ||
183240
github.event_name == 'schedule' && github.event.schedule == '0 */3 * * *'
@@ -202,12 +259,22 @@ jobs:
202259
restore-keys: ${{ runner.os }}-go-
203260
- run: go mod download
204261
- name: Nuke global resources
262+
id: nuke
205263
run: |
264+
set +e
206265
go run -ldflags="-X 'main.VERSION=${{ github.sha }}'" main.go aws \
207266
--older-than 2h --force --config ./.github/nuke_config.yml \
208267
--region global ${{ env.COMMON_EXCLUDES }} \
209268
--exclude-resource-type internet-gateway \
210-
--delete-unaliased-kms-keys --log-level info
269+
--delete-unaliased-kms-keys --log-level info 2>&1 | tee /tmp/nuke.log
270+
EXIT_CODE=${PIPESTATUS[0]}
271+
272+
DELETED=$(grep -c "^\s*INFO\s*\[Deleted\]" /tmp/nuke.log || echo "0")
273+
ERRORS=$(grep -c "^\s*ERROR\s*\[Failed\]" /tmp/nuke.log || echo "0")
274+
echo "deleted_count=${DELETED}" >> $GITHUB_OUTPUT
275+
echo "error_count=${ERRORS}" >> $GITHUB_OUTPUT
276+
277+
exit $EXIT_CODE
211278
212279
configtests_regional:
213280
name: "ConfigTests: ${{ matrix.region }}"
@@ -241,12 +308,29 @@ jobs:
241308
restore-keys: ${{ runner.os }}-go-
242309
- run: go mod download
243310
- name: Nuke ${{ matrix.region }}
311+
id: nuke
244312
run: |
313+
set +e
245314
go run -ldflags="-X 'main.VERSION=${{ github.sha }}'" main.go aws \
246315
--older-than 2h --force --config ./.github/nuke_config.yml \
247316
--region ${{ matrix.region }} ${{ env.COMMON_EXCLUDES }} \
248317
--exclude-resource-type internet-gateway \
249-
--delete-unaliased-kms-keys --log-level info
318+
--delete-unaliased-kms-keys --log-level info 2>&1 | tee /tmp/nuke.log
319+
EXIT_CODE=${PIPESTATUS[0]}
320+
321+
DELETED=$(grep -c "^\s*INFO\s*\[Deleted\]" /tmp/nuke.log || echo "0")
322+
ERRORS=$(grep -c "^\s*ERROR\s*\[Failed\]" /tmp/nuke.log || echo "0")
323+
echo "deleted_count=${DELETED}" >> $GITHUB_OUTPUT
324+
echo "error_count=${ERRORS}" >> $GITHUB_OUTPUT
325+
326+
exit $EXIT_CODE
327+
- name: Upload results
328+
if: always()
329+
uses: actions/upload-artifact@v4
330+
with:
331+
name: configtests-${{ matrix.region }}
332+
path: /tmp/nuke.log
333+
retention-days: 7
250334

251335
configtests_notify:
252336
name: "ConfigTests: Notify"
@@ -262,12 +346,30 @@ jobs:
262346
with:
263347
role-to-assume: arn:aws:iam::087285199408:role/cloud-nuke-gha
264348
aws-region: us-east-1
265-
- name: Send Slack notification
349+
- name: Download all artifacts
350+
uses: actions/download-artifact@v4
351+
with:
352+
pattern: configtests-*
353+
path: /tmp/logs
354+
merge-multiple: true
355+
continue-on-error: true
356+
- name: Aggregate and notify
266357
run: |
267358
WEBHOOK_URL=$(aws secretsmanager get-secret-value \
268359
--secret-id cloud-nuke/slack-webhook \
269360
--query SecretString --output text)
270361
362+
TOTAL_DELETED=0
363+
TOTAL_ERRORS=0
364+
if [ -d /tmp/logs ]; then
365+
TOTAL_DELETED=$(grep -rh "^\s*INFO\s*\[Deleted\]" /tmp/logs 2>/dev/null | wc -l || echo "0")
366+
TOTAL_ERRORS=$(grep -rh "^\s*ERROR\s*\[Failed\]" /tmp/logs 2>/dev/null | wc -l || echo "0")
367+
fi
368+
GLOBAL_DELETED="${{ needs.configtests_global.outputs.deleted_count }}"
369+
GLOBAL_ERRORS="${{ needs.configtests_global.outputs.error_count }}"
370+
TOTAL_DELETED=$((TOTAL_DELETED + ${GLOBAL_DELETED:-0}))
371+
TOTAL_ERRORS=$((TOTAL_ERRORS + ${GLOBAL_ERRORS:-0}))
372+
271373
if [ "${{ needs.configtests_global.result }}" == "success" ] && \
272374
[ "${{ needs.configtests_regional.result }}" == "success" ]; then
273375
STATUS="✅ Success"
@@ -277,6 +379,13 @@ jobs:
277379
COLOR="danger"
278380
fi
279381
382+
MSG="*ConfigTests Nuke*: ${STATUS}"
383+
MSG="${MSG}\nDeleted: ${TOTAL_DELETED} resources"
384+
if [ "$TOTAL_ERRORS" -gt 0 ]; then
385+
MSG="${MSG} | Errors: ${TOTAL_ERRORS}"
386+
fi
387+
MSG="${MSG}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
388+
280389
curl -sS -X POST "$WEBHOOK_URL" -H "Content-Type: application/json" -d @- <<EOF
281390
{
282391
"attachments": [{
@@ -285,7 +394,7 @@ jobs:
285394
"type": "section",
286395
"text": {
287396
"type": "mrkdwn",
288-
"text": "*ConfigTests Nuke*: ${STATUS}\nGlobal: ${{ needs.configtests_global.result }} | Regional: ${{ needs.configtests_regional.result }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
397+
"text": "${MSG}"
289398
}
290399
}]
291400
}]
@@ -299,6 +408,9 @@ jobs:
299408
name: "Sandbox: Global"
300409
runs-on: ubuntu-latest
301410
timeout-minutes: 30
411+
outputs:
412+
deleted_count: ${{ steps.nuke.outputs.deleted_count }}
413+
error_count: ${{ steps.nuke.outputs.error_count }}
302414
if: |
303415
github.event_name == 'workflow_dispatch' && (inputs.account == 'sandbox' || inputs.account == '') ||
304416
github.event_name == 'schedule' && github.event.schedule == '0 0 * * *'
@@ -323,12 +435,22 @@ jobs:
323435
restore-keys: ${{ runner.os }}-go-
324436
- run: go mod download
325437
- name: Nuke global resources
438+
id: nuke
326439
run: |
440+
set +e
327441
go run -ldflags="-X 'main.VERSION=${{ github.sha }}'" main.go aws \
328442
--older-than 24h --force --config ./.github/nuke_config.yml \
329443
--region global ${{ env.COMMON_EXCLUDES }} \
330444
--exclude-resource-type eip \
331-
--delete-unaliased-kms-keys --log-level info
445+
--delete-unaliased-kms-keys --log-level info 2>&1 | tee /tmp/nuke.log
446+
EXIT_CODE=${PIPESTATUS[0]}
447+
448+
DELETED=$(grep -c "^\s*INFO\s*\[Deleted\]" /tmp/nuke.log || echo "0")
449+
ERRORS=$(grep -c "^\s*ERROR\s*\[Failed\]" /tmp/nuke.log || echo "0")
450+
echo "deleted_count=${DELETED}" >> $GITHUB_OUTPUT
451+
echo "error_count=${ERRORS}" >> $GITHUB_OUTPUT
452+
453+
exit $EXIT_CODE
332454
333455
sandbox_regional:
334456
name: "Sandbox: ${{ matrix.region }}"
@@ -362,12 +484,29 @@ jobs:
362484
restore-keys: ${{ runner.os }}-go-
363485
- run: go mod download
364486
- name: Nuke ${{ matrix.region }}
487+
id: nuke
365488
run: |
489+
set +e
366490
go run -ldflags="-X 'main.VERSION=${{ github.sha }}'" main.go aws \
367491
--older-than 24h --force --config ./.github/nuke_config.yml \
368492
--region ${{ matrix.region }} ${{ env.COMMON_EXCLUDES }} \
369493
--exclude-resource-type eip \
370-
--delete-unaliased-kms-keys --log-level info
494+
--delete-unaliased-kms-keys --log-level info 2>&1 | tee /tmp/nuke.log
495+
EXIT_CODE=${PIPESTATUS[0]}
496+
497+
DELETED=$(grep -c "^\s*INFO\s*\[Deleted\]" /tmp/nuke.log || echo "0")
498+
ERRORS=$(grep -c "^\s*ERROR\s*\[Failed\]" /tmp/nuke.log || echo "0")
499+
echo "deleted_count=${DELETED}" >> $GITHUB_OUTPUT
500+
echo "error_count=${ERRORS}" >> $GITHUB_OUTPUT
501+
502+
exit $EXIT_CODE
503+
- name: Upload results
504+
if: always()
505+
uses: actions/upload-artifact@v4
506+
with:
507+
name: sandbox-${{ matrix.region }}
508+
path: /tmp/nuke.log
509+
retention-days: 7
371510

372511
sandbox_notify:
373512
name: "Sandbox: Notify"
@@ -383,12 +522,30 @@ jobs:
383522
with:
384523
role-to-assume: arn:aws:iam::087285199408:role/cloud-nuke-gha
385524
aws-region: us-east-1
386-
- name: Send Slack notification
525+
- name: Download all artifacts
526+
uses: actions/download-artifact@v4
527+
with:
528+
pattern: sandbox-*
529+
path: /tmp/logs
530+
merge-multiple: true
531+
continue-on-error: true
532+
- name: Aggregate and notify
387533
run: |
388534
WEBHOOK_URL=$(aws secretsmanager get-secret-value \
389535
--secret-id cloud-nuke/slack-webhook \
390536
--query SecretString --output text)
391537
538+
TOTAL_DELETED=0
539+
TOTAL_ERRORS=0
540+
if [ -d /tmp/logs ]; then
541+
TOTAL_DELETED=$(grep -rh "^\s*INFO\s*\[Deleted\]" /tmp/logs 2>/dev/null | wc -l || echo "0")
542+
TOTAL_ERRORS=$(grep -rh "^\s*ERROR\s*\[Failed\]" /tmp/logs 2>/dev/null | wc -l || echo "0")
543+
fi
544+
GLOBAL_DELETED="${{ needs.sandbox_global.outputs.deleted_count }}"
545+
GLOBAL_ERRORS="${{ needs.sandbox_global.outputs.error_count }}"
546+
TOTAL_DELETED=$((TOTAL_DELETED + ${GLOBAL_DELETED:-0}))
547+
TOTAL_ERRORS=$((TOTAL_ERRORS + ${GLOBAL_ERRORS:-0}))
548+
392549
if [ "${{ needs.sandbox_global.result }}" == "success" ] && \
393550
[ "${{ needs.sandbox_regional.result }}" == "success" ]; then
394551
STATUS="✅ Success"
@@ -398,6 +555,13 @@ jobs:
398555
COLOR="danger"
399556
fi
400557
558+
MSG="*Sandbox Nuke*: ${STATUS}"
559+
MSG="${MSG}\nDeleted: ${TOTAL_DELETED} resources"
560+
if [ "$TOTAL_ERRORS" -gt 0 ]; then
561+
MSG="${MSG} | Errors: ${TOTAL_ERRORS}"
562+
fi
563+
MSG="${MSG}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
564+
401565
curl -sS -X POST "$WEBHOOK_URL" -H "Content-Type: application/json" -d @- <<EOF
402566
{
403567
"attachments": [{
@@ -406,7 +570,7 @@ jobs:
406570
"type": "section",
407571
"text": {
408572
"type": "mrkdwn",
409-
"text": "*Sandbox Nuke*: ${STATUS}\nGlobal: ${{ needs.sandbox_global.result }} | Regional: ${{ needs.sandbox_regional.result }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
573+
"text": "${MSG}"
410574
}
411575
}]
412576
}]

0 commit comments

Comments
 (0)