Skip to content

Instantly share code, notes, and snippets.

@filipeandre
Last active June 19, 2025 00:47
Show Gist options
  • Save filipeandre/3f8d3147a503b6a3fda7aa46be896857 to your computer and use it in GitHub Desktop.
Save filipeandre/3f8d3147a503b6a3fda7aa46be896857 to your computer and use it in GitHub Desktop.
Clone delete stack
#!/bin/bash
# Usage:
# ./clone_deleted_stack.sh <stack-id-or-name> [new-stack-name] [optional-role-arn]
set -euo pipefail
INPUT_ID="${1:-}"
NEW_STACK_NAME="${2:-}"
ROLE_ARN="${3:-}"
ARN_REGEX="^arn:aws:cloudformation:([^:]+):[^:]+:stack/([^/]+)/.+$"
if [[ -z "$INPUT_ID" ]]; then
echo "Usage: $0 <stack-id-or-name> [new-stack-name] [optional-role-arn]"
exit 1
fi
# Detect if input is ARN and extract region + name
if [[ "$INPUT_ID" =~ $ARN_REGEX ]]; then
REGION="${BASH_REMATCH[1]}"
DELETED_STACK_NAME="${BASH_REMATCH[2]}"
STACK_IDENTIFIER="$INPUT_ID"
else
DELETED_STACK_NAME="$INPUT_ID"
REGION="${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region)}}"
if [[ -z "$REGION" ]]; then
echo "❌ Unable to determine AWS region. Please set AWS_REGION, AWS_DEFAULT_REGION, or configure a default profile."
exit 1
fi
STACK_IDENTIFIER="$DELETED_STACK_NAME"
fi
NEW_STACK_NAME="${NEW_STACK_NAME:-$DELETED_STACK_NAME}"
PARAMS_FILE="cf_params_${DELETED_STACK_NAME}.json"
TEMPLATE_FILE="cf_template_${DELETED_STACK_NAME}.json"
# Globals for cleanup
TEMP_BUCKET=""
TEMPLATE_OBJECT="template.json"
cleanup() {
if [[ -n "$TEMP_BUCKET" ]]; then
echo "[+] Cleaning up temporary S3 bucket..."
aws s3 rm "s3://${TEMP_BUCKET}/${TEMPLATE_OBJECT}" --region "$REGION" || true
aws s3 rb "s3://${TEMP_BUCKET}" --region "$REGION" || true
fi
}
trap cleanup EXIT
echo "[+] Fetching deleted stack metadata:"
echo " Stack ID: $STACK_IDENTIFIER"
echo " Region: $REGION"
echo " New Stack: $NEW_STACK_NAME"
[[ -n "$ROLE_ARN" ]] && echo " Role ARN: $ROLE_ARN"
# Get stack info
stack_info=$(aws cloudformation describe-stacks \
--stack-name "$STACK_IDENTIFIER" \
--region "$REGION" 2>/dev/null)
if [[ -z "$stack_info" ]]; then
echo "❌ Could not retrieve stack metadata."
exit 1
fi
# Extract parameters
echo "[+] Extracting parameters to $PARAMS_FILE..."
echo "$stack_info" | jq -r '.Stacks[0].Parameters' > "$PARAMS_FILE"
# Extract template body
echo "[+] Getting template body..."
aws cloudformation get-template \
--stack-name "$STACK_IDENTIFIER" \
--region "$REGION" \
--query 'TemplateBody' \
--output text > "$TEMPLATE_FILE"
# Determine if template is too large
MAX_DIRECT_SIZE=51200
TEMPLATE_SIZE=$(wc -c <"$TEMPLATE_FILE")
if [[ "$TEMPLATE_SIZE" -gt "$MAX_DIRECT_SIZE" ]]; then
echo "[+] Template too large ($TEMPLATE_SIZE bytes). Using S3..."
ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
TEMP_BUCKET="cf-temp-${ACCOUNT_ID}-$(date +%s)"
aws s3 mb "s3://${TEMP_BUCKET}" --region "$REGION"
aws s3 cp "$TEMPLATE_FILE" "s3://${TEMP_BUCKET}/${TEMPLATE_OBJECT}" --region "$REGION"
TEMPLATE_SOURCE=(--template-url "https://s3.${REGION}.amazonaws.com/${TEMP_BUCKET}/${TEMPLATE_OBJECT}")
else
echo "[+] Template size acceptable ($TEMPLATE_SIZE bytes). Using direct body..."
TEMPLATE_SOURCE=(--template-body "file://$TEMPLATE_FILE")
fi
# Prepare stack creation command
CREATE_CMD=(
aws cloudformation create-stack
--stack-name "$NEW_STACK_NAME"
"${TEMPLATE_SOURCE[@]}"
--parameters "file://$PARAMS_FILE"
--capabilities CAPABILITY_NAMED_IAM
--region "$REGION"
)
[[ -n "$ROLE_ARN" ]] && CREATE_CMD+=(--role-arn "$ROLE_ARN")
# Deploy the new stack
echo "[+] Deploying new stack..."
"${CREATE_CMD[@]}"
echo "✅ Stack $NEW_STACK_NAME successfully deployed."
@filipeandre
Copy link
Author

filipeandre commented Jun 16, 2025

bash <(curl -s https://gist.githubusercontent.com/filipeandre/3f8d3147a503b6a3fda7aa46be896857/raw/a1c5208bd870b3b5b5997cfd25a9ce021bd12e22/clone_deleted_stack.sh) STACK_ARN

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment