Skip to content

Nuke: PhxDevOps

Nuke: PhxDevOps #12

name: "Nuke: PhxDevOps"
on:
schedule:
- cron: '0 */3 * * *' # Every 3 hours
workflow_dispatch:
permissions:
id-token: write
contents: read
env:
MISE_VERSION: '2025.12.10'
ACCOUNT_NAME: PhxDevOps
ROLE_ARN: arn:aws:iam::087285199408:role/cloud-nuke-gha
OLDER_THAN: 2h
COMMON_EXCLUDES: >-
--exclude-resource-type iam
--exclude-resource-type iam-group
--exclude-resource-type iam-policy
--exclude-resource-type iam-role
--exclude-resource-type iam-service-linked-role
--exclude-resource-type oidcprovider
--exclude-resource-type route53-hosted-zone
--exclude-resource-type route53-cidr-collection
--exclude-resource-type route53-traffic-policy
--exclude-resource-type ecr
--exclude-resource-type config-rules
--exclude-resource-type nat-gateway
--exclude-resource-type ec2-subnet
jobs:
# Nuke global resources (IAM, S3, Route53, etc.) - runs in us-east-1
global:
name: "PhxDevOps: Global"
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.ROLE_ARN }}
aws-region: us-east-1
- uses: jdx/mise-action@v3
with:
version: ${{ env.MISE_VERSION }}
experimental: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- run: go mod download
- name: Nuke global resources
id: nuke
env:
DISABLE_TELEMETRY: true
run: |
set +e
go run -ldflags="-X 'main.VERSION=${{ github.sha }}'" main.go aws \
--older-than ${{ env.OLDER_THAN }} --force --config ./.github/nuke_config.yml \
--region global ${{ env.COMMON_EXCLUDES }} \
--delete-unaliased-kms-keys --log-level info \
--output-format json --output-file /tmp/nuke-global.json 2>&1 | tee /tmp/nuke-global.log
EXIT_CODE=${PIPESTATUS[0]}
if [ -f /tmp/nuke-global.json ]; then
DELETED=$(jq -r '.summary.deleted // 0' /tmp/nuke-global.json)
ERRORS=$(jq -r '.summary.failed // 0' /tmp/nuke-global.json)
else
DELETED=0
ERRORS=0
fi
echo "deleted_count=${DELETED}" >> $GITHUB_OUTPUT
echo "error_count=${ERRORS}" >> $GITHUB_OUTPUT
exit $EXIT_CODE
- name: Upload global results
if: always()
uses: actions/upload-artifact@v4
with:
name: PhxDevOps-global
path: |
/tmp/nuke-global.log
/tmp/nuke-global.json
retention-days: 7
# Nuke regional resources across all supported regions in parallel
regional:
name: "PhxDevOps: ${{ matrix.region }}"
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
region:
- ap-northeast-1
- ap-northeast-2
- ap-northeast-3
- ap-south-1
- ap-southeast-1
- ap-southeast-2
- ca-central-1
- eu-central-1
- eu-north-1
- eu-west-1
- eu-west-2
- eu-west-3
# - me-central-1 # Opt-in region, not enabled in all accounts
- sa-east-1
- us-east-1
- us-east-2
- us-west-1
- us-west-2
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.ROLE_ARN }}
aws-region: ${{ matrix.region }}
- uses: jdx/mise-action@v3
with:
version: ${{ env.MISE_VERSION }}
experimental: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- run: go mod download
- name: Nuke ${{ matrix.region }}
id: nuke
env:
DISABLE_TELEMETRY: true
run: |
set +e
go run -ldflags="-X 'main.VERSION=${{ github.sha }}'" main.go aws \
--older-than ${{ env.OLDER_THAN }} --force --config ./.github/nuke_config.yml \
--region ${{ matrix.region }} ${{ env.COMMON_EXCLUDES }} \
--delete-unaliased-kms-keys --log-level info \
--output-format json --output-file /tmp/nuke-${{ matrix.region }}.json 2>&1 | tee /tmp/nuke-${{ matrix.region }}.log
EXIT_CODE=${PIPESTATUS[0]}
if [ -f /tmp/nuke-${{ matrix.region }}.json ]; then
DELETED=$(jq -r '.summary.deleted // 0' /tmp/nuke-${{ matrix.region }}.json)
ERRORS=$(jq -r '.summary.failed // 0' /tmp/nuke-${{ matrix.region }}.json)
else
DELETED=0
ERRORS=0
fi
echo "deleted_count=${DELETED}" >> $GITHUB_OUTPUT
echo "error_count=${ERRORS}" >> $GITHUB_OUTPUT
exit $EXIT_CODE
- name: Upload results
if: always()
uses: actions/upload-artifact@v4
with:
name: PhxDevOps-${{ matrix.region }}
path: |
/tmp/nuke-${{ matrix.region }}.log
/tmp/nuke-${{ matrix.region }}.json
retention-days: 7
# Aggregate results and send Slack notification
notify:
name: "PhxDevOps: Notify"
runs-on: ubuntu-latest
if: always()
needs: [global, regional]
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.ROLE_ARN }}
aws-region: us-east-1
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
pattern: PhxDevOps-*
path: /tmp/logs
merge-multiple: true
continue-on-error: true
- name: Aggregate and notify
run: |
WEBHOOK_URL=$(aws secretsmanager get-secret-value \
--secret-id cloud-nuke/slack-webhook \
--query SecretString --output text)
TOTAL_DELETED=0
TOTAL_FAILED=0
while IFS= read -r json_file; do
if [ -f "$json_file" ]; then
DELETED=$(jq -r '.summary.deleted // 0' "$json_file")
FAILED=$(jq -r '.summary.failed // 0' "$json_file")
TOTAL_DELETED=$((TOTAL_DELETED + DELETED))
TOTAL_FAILED=$((TOTAL_FAILED + FAILED))
fi
done < <(find /tmp/logs -name "*.json" 2>/dev/null)
if [ "${{ needs.global.result }}" == "success" ] && \
[ "${{ needs.regional.result }}" == "success" ]; then
STATUS="Success"
COLOR="good"
else
STATUS="Failed"
COLOR="danger"
fi
MSG="*PhxDevOps Nuke*: ${STATUS}"
MSG="${MSG}\nDeleted: ${TOTAL_DELETED:-0} | Failed: ${TOTAL_FAILED:-0}"
MSG="${MSG}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
curl -sS -X POST "$WEBHOOK_URL" -H "Content-Type: application/json" -d @- <<EOF
{
"attachments": [{
"color": "${COLOR}",
"blocks": [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "${MSG}"
}
}]
}]
}
EOF