Nuke: PhxDevOps #10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |