Something went wrong. Try again.
Downloads all tfstate files from an organization's workspaces
Something went wrong. Try again.
3.3 kB · 95 lines · Created 8mo ago
Shell
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596#!/bin/bash## Terraform Cloud State Backup# Downloads all tfstate files from an organization's workspaces# Usage: ./backup-tfstate.sh <API_TOKEN> <ORG_NAME> [MAX_PARALLEL]#if [ $# -lt 2 ]; then echo "Usage: $0 <API_TOKEN> <ORG_NAME> [MAX_PARALLEL]" exit 1ficommand -v jq >/dev/null || { echo "Error: jq is required"; exit 1; }API_TOKEN="$1"ORG_NAME="$2"MAX_PARALLEL="${3:-10}"API_BASE="https://app.terraform.io/api/v2"BACKUP_DIR="./tfstate-backups-${ORG_NAME}-$(date +%Y%m%d-%H%M%S)"mkdir -p "$BACKUP_DIR"RESULTS_FILE="$BACKUP_DIR/.results"WORKSPACES_FILE="$BACKUP_DIR/.workspaces"> "$RESULTS_FILE"> "$WORKSPACES_FILE"echo "Fetching workspaces for: $ORG_NAME"page=1while true; do url="${API_BASE}/organizations/${ORG_NAME}/workspaces?page%5Bnumber%5D=${page}&page%5Bsize%5D=100" response=$(curl -s -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/vnd.api+json" "$url") echo "$response" | jq -r '.data[] | "\(.id)\t\(.attributes.name)"' >> "$WORKSPACES_FILE" next=$(echo "$response" | jq -r '.meta.pagination["next-page"] // empty') [ -z "$next" ] && break page=$nextdonetotal=$(wc -l < "$WORKSPACES_FILE" | tr -d ' ')echo "Found $total workspaces"echo "Downloading states (max $MAX_PARALLEL parallel)..."echo ""download_state() { local ws_id="$1" local ws_name="$2" local safe_name=$(echo "$ws_name" | tr '/ ' '__') local output_file="${BACKUP_DIR}/${ORG_NAME}_${safe_name}.tfstate" local tmp_file="/tmp/state_${ws_id}.json" local state_http=$(curl -s -o "$tmp_file" -w "%{http_code}" \ -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/vnd.api+json" \ "${API_BASE}/workspaces/${ws_id}/current-state-version") if [ "$state_http" != "200" ]; then echo "[skip:$state_http] $ws_name" echo "skip" >> "$RESULTS_FILE" rm -f "$tmp_file" return fi local download_url=$(jq -r '.data.attributes["hosted-state-download-url"] // empty' "$tmp_file") rm -f "$tmp_file" if [ -z "$download_url" ]; then echo "[skip:no-url] $ws_name" echo "skip" >> "$RESULTS_FILE" return fi local http_code=$(curl -s -L -o "$output_file" -w "%{http_code}" -H "Authorization: Bearer $API_TOKEN" "$download_url") if [ "$http_code" = "200" ]; then echo "[done] $ws_name" echo "success" >> "$RESULTS_FILE" else echo "[fail:$http_code] $ws_name" echo "fail" >> "$RESULTS_FILE" rm -f "$output_file" fi}export -f download_stateexport API_TOKEN API_BASE BACKUP_DIR RESULTS_FILE ORG_NAMEwhile IFS=$'\t' read -r ws_id ws_name; do download_state "$ws_id" "$ws_name" & while [ $(jobs -r | wc -l) -ge "$MAX_PARALLEL" ]; do sleep 0.1 donedone < "$WORKSPACES_FILE"waitsuccess_count=$(grep -c "success" "$RESULTS_FILE" 2>/dev/null) || success_count=0skip_count=$(grep -c "skip" "$RESULTS_FILE" 2>/dev/null) || skip_count=0fail_count=$(grep -c "fail" "$RESULTS_FILE" 2>/dev/null) || fail_count=0echo ""echo "Complete: $BACKUP_DIR"echo " Total: $total"echo " Success: $success_count"echo " Skipped: $skip_count"echo " Failed: $fail_count"rm -f "$RESULTS_FILE" "$WORKSPACES_FILE"