#!/usr/bin/env bash # # delete-terraform-substrate-prod-dev.sh — PLAT-147 live delete of # seahaven-terraform-substrate in prod (011934824531) and/or dev (710827005802). # Does not touch terraform-substrate-external-dev (396287094661). # # Gate: every prod/dev hcptf-* apply role MUST already have detached # seahaven-hcptf-iam-management (consumer import PRs). OIDC is Retain and # survives. The guardrail policy is deleted with the stack. # # Do not run until: # 1. Each of the eight prod apply roles is imported in app Terraform. # 2. A substrate update with DeletionPolicy: Retain has removed those roles # from the template (CFN forgets them without deleting). # 3. terraform-substrate-prod and terraform-substrate-dev are removed from # bin/app.ts and .github/workflows/deploy.yaml so CD cannot recreate them. # # Usage: # scripts/delete-terraform-substrate-prod-dev.sh --account prod|dev --inventory # scripts/delete-terraform-substrate-prod-dev.sh --account prod|dev --yes # set -euo pipefail ACCOUNT_KEY="" INVENTORY=0 ASSUME_YES=0 while [[ $# -gt 0 ]]; do case "$1" in --account) ACCOUNT_KEY="$2"; shift 2 ;; --inventory) INVENTORY=1; shift ;; --yes|-y) ASSUME_YES=1; shift ;; -h|--help) sed -n '2,24p' "$0"; exit 0 ;; -*) echo "unknown flag: $1" >&2; exit 2 ;; *) echo "unexpected argument: $1" >&2; exit 2 ;; esac done case "$ACCOUNT_KEY" in prod) ACCOUNT_ID="011934824531" ;; dev) ACCOUNT_ID="710827005802" ;; *) echo "usage: $0 --account prod|dev [--inventory|--yes]" >&2 exit 2 ;; esac if [[ "$ACCOUNT_ID" == "396287094661" ]]; then echo "refusing: external-dev is out of scope (PLAT-148)" >&2 exit 2 fi ORIG_AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID-}" ORIG_AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY-}" ORIG_AWS_SESSION_TOKEN="${AWS_SESSION_TOKEN-}" restore_creds() { if [[ -n "${ORIG_AWS_ACCESS_KEY_ID}" ]]; then export AWS_ACCESS_KEY_ID="$ORIG_AWS_ACCESS_KEY_ID" export AWS_SECRET_ACCESS_KEY="$ORIG_AWS_SECRET_ACCESS_KEY" export AWS_SESSION_TOKEN="$ORIG_AWS_SESSION_TOKEN" else unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN fi } trap restore_creds EXIT CREDS="$(aws sts assume-role \ --role-arn "arn:aws:iam::${ACCOUNT_ID}:role/OrganizationAccountAccessRole" \ --role-session-name plat-147-tf-substrate \ --query Credentials --output json)" export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_ACCESS_KEY_ID="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["AccessKeyId"])' <<<"$CREDS")" AWS_SECRET_ACCESS_KEY="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["SecretAccessKey"])' <<<"$CREDS")" AWS_SESSION_TOKEN="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["SessionToken"])' <<<"$CREDS")" echo "account: ${ACCOUNT_ID} (${ACCOUNT_KEY})" echo "caller: $(aws sts get-caller-identity --query Arn --output text)" POLICY_ARN="arn:aws:iam::${ACCOUNT_ID}:policy/seahaven-hcptf-iam-management" ATTACHED="[]" if aws iam get-policy --policy-arn "$POLICY_ARN" >/dev/null 2>&1; then ATTACHED="$(aws iam list-entities-for-policy --policy-arn "$POLICY_ARN" --query 'PolicyRoles[].RoleName' --output json)" fi echo "seahaven-hcptf-iam-management attachments: $ATTACHED" HCPS="$(aws iam list-roles --query 'Roles[?starts_with(RoleName, `hcptf-`)].RoleName' --output json)" echo "hcptf-* roles still present: $HCPS" OIDC="$(aws iam list-open-id-connect-providers --query 'OpenIDConnectProviderList[?contains(Arn, `app.terraform.io`)].Arn' --output json)" echo "app.terraform.io OIDC: $OIDC" if [[ "$INVENTORY" -eq 1 ]]; then exit 0 fi python3 - "$ATTACHED" <<'PY' import json, sys roles = json.loads(sys.argv[1]) if roles: raise SystemExit(f"refusing delete: seahaven-hcptf-iam-management still attached to {roles}") PY if [[ "$ASSUME_YES" -eq 0 ]]; then read -r -p "delete CloudFormation stack seahaven-terraform-substrate in ${ACCOUNT_ID}? [y/N] " ans [[ "$ans" =~ ^[Yy]$ ]] || { echo "skipped"; exit 0; } fi echo "deleting seahaven-terraform-substrate (OIDC DeletionPolicy=Retain)" aws cloudformation delete-stack --stack-name seahaven-terraform-substrate aws cloudformation wait stack-delete-complete --stack-name seahaven-terraform-substrate echo "stack gone. confirm OIDC still exists:" aws iam list-open-id-connect-providers --query 'OpenIDConnectProviderList[?contains(Arn, `app.terraform.io`)].Arn' --output text