Last active
June 26, 2024 07:37
-
-
Save quangnhut123/ad7082e5c60fa4be0eb1f7161aecb7c7 to your computer and use it in GitHub Desktop.
This file contains 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
#!/bin/bash | |
# Set up your environment variables | |
TFC_ORG_SOURCE="" | |
TFC_ORG_DEST="" | |
TFC_TOKEN_SOURCE="" | |
TFC_TOKEN_DEST="" | |
# Define the list of workspace names to exclude | |
EXCLUDE_WORKSPACES=("workspace_1" "workspace_2") | |
# Function to check if a workspace name contains any of the exclusion substrings | |
is_excluded() { | |
local workspace_name=$1 | |
for excluded in "${EXCLUDE_WORKSPACES[@]}"; do | |
if [[ "$workspace_name" == *"$excluded"* ]]; then | |
return 0 | |
fi | |
done | |
return 1 | |
} | |
# Function to get the total number of pages | |
get_total_pages() { | |
local org=$1 | |
local token=$2 | |
curl --silent \ | |
--header "Authorization: Bearer $token" \ | |
--header "Content-Type: application/vnd.api+json" \ | |
"https://app.terraform.io/api/v2/organizations/$org/workspaces" \ | |
| jq -r '.meta.pagination."total-pages"' | |
} | |
# Function to get workspaces for a specific page | |
get_workspaces_for_page() { | |
local org=$1 | |
local token=$2 | |
local page_number=$3 | |
curl --silent \ | |
--header "Authorization: Bearer $token" \ | |
--header "Content-Type: application/vnd.api+json" \ | |
"https://app.terraform.io/api/v2/organizations/$org/workspaces?page%5Bnumber%5D=$page_number" \ | |
| jq -c '.data[] | {id: .id, name: .attributes.name}' | |
} | |
# Function to get the state download URL for a workspace | |
get_state_url() { | |
local workspace_id=$1 | |
local token=$2 | |
curl --silent \ | |
--header "Authorization: Bearer $token" \ | |
--header "Content-Type: application/vnd.api+json" \ | |
"https://app.terraform.io/api/v2/workspaces/$workspace_id/current-state-version" \ | |
| jq -r '.data.attributes."hosted-state-download-url"' | |
} | |
# Function to get workspace ID by name in the destination organization | |
get_workspace_id() { | |
local org=$1 | |
local workspace=$2 | |
local token=$3 | |
curl --silent \ | |
--header "Authorization: Bearer $token" \ | |
--header "Content-Type: application/vnd.api+json" \ | |
"https://app.terraform.io/api/v2/organizations/$org/workspaces/$workspace" \ | |
| jq -r '.data.id' | |
} | |
# Function to upload state to a workspace in the destination organization | |
upload_state() { | |
local workspace_id=$1 | |
local token=$2 | |
local state_file=$3 | |
curl --silent \ | |
--header "Authorization: Bearer $token" \ | |
--header "Content-Type: application/vnd.api+json" \ | |
--request POST \ | |
--data @- \ | |
"https://app.terraform.io/api/v2/workspaces/$workspace_id/state-versions" <<EOF | |
{ | |
"data": { | |
"type": "state-versions", | |
"attributes": { | |
"serial": 0, | |
"md5": "$(md5 -q $state_file)", | |
"state": "$(base64 < $state_file)" | |
} | |
} | |
} | |
EOF | |
} | |
# Get the total number of pages | |
echo "Fetching total number of pages..." | |
TOTAL_PAGES=$(get_total_pages "$TFC_ORG_SOURCE" "$TFC_TOKEN_SOURCE") | |
if [ -z "$TOTAL_PAGES" ]; then | |
echo "Failed to fetch total number of pages." | |
exit 1 | |
fi | |
echo "Total pages: $TOTAL_PAGES" | |
# Collect all workspaces across all pages | |
workspaces=() | |
for (( page_number=1; page_number<=TOTAL_PAGES; page_number++ )); do | |
echo "Fetching workspaces for page $page_number..." | |
current_page_workspaces=$(get_workspaces_for_page "$TFC_ORG_SOURCE" "$TFC_TOKEN_SOURCE" "$page_number") | |
while IFS= read -r workspace; do | |
workspaces+=("$workspace") | |
done <<< "$current_page_workspaces" | |
done | |
# Join all workspaces into a single JSON array | |
WORKSPACES_JSON=$(printf '%s\n' "${workspaces[@]}" | jq -s '.') | |
if [ -z "$WORKSPACES_JSON" ]; then | |
echo "Failed to fetch workspaces or no workspaces found." | |
exit 1 | |
fi | |
echo "Workspaces fetched successfully." | |
# echo "Workspaces JSON: $WORKSPACES_JSON" | |
# Iterate through each workspace, download its state, and upload it to the destination organization | |
echo "$WORKSPACES_JSON" | jq -c '.[]' | while read -r workspace; do | |
WORKSPACE_ID=$(echo "$workspace" | jq -r '.id') | |
WORKSPACE_NAME=$(echo "$workspace" | jq -r '.name') | |
# Check if the workspace is in the exclusion list | |
if is_excluded "$WORKSPACE_NAME"; then | |
echo "Skipping excluded workspace: $WORKSPACE_NAME" | |
continue | |
fi | |
echo "Processing workspace: $WORKSPACE_NAME (ID: $WORKSPACE_ID)" | |
STATE_URL=$(get_state_url "$WORKSPACE_ID" "$TFC_TOKEN_SOURCE") | |
if [ -z "$STATE_URL" ]; then | |
echo "Failed to get state download URL for workspace: $WORKSPACE_NAME" | |
continue | |
fi | |
echo "State download URL for $WORKSPACE_NAME: $STATE_URL" | |
curl -L --silent --header "Authorization: Bearer $TFC_TOKEN_SOURCE" -o "${WORKSPACE_NAME}_state.tfstate" "$STATE_URL" | |
if [ $? -ne 0 ] || [ ! -s "${WORKSPACE_NAME}_state.tfstate" ]; then | |
echo "Failed to download state for workspace: $WORKSPACE_NAME or state file is empty." | |
continue | |
fi | |
echo "State downloaded successfully for workspace: $WORKSPACE_NAME" | |
DEST_WORKSPACE_ID=$(get_workspace_id "$TFC_ORG_DEST" "$WORKSPACE_NAME" "$TFC_TOKEN_DEST") | |
if [ "$DEST_WORKSPACE_ID" == null ]; then | |
echo "Failed to get destination workspace ID for workspace: $WORKSPACE_NAME" | |
continue | |
fi | |
echo "Destination workspace ID for $WORKSPACE_NAME: $DEST_WORKSPACE_ID" | |
upload_response=$(upload_state "$DEST_WORKSPACE_ID" "$TFC_TOKEN_DEST" "${WORKSPACE_NAME}_state.tfstate") | |
if echo "$upload_response" | grep -q '"errors"'; then | |
echo "Failed to upload state to destination workspace: $WORKSPACE_NAME" | |
echo "Response: $upload_response" | |
continue | |
fi | |
echo "State uploaded successfully to destination workspace: $WORKSPACE_NAME" | |
# Clean up the downloaded state file | |
rm "${WORKSPACE_NAME}_state.tfstate" | |
done | |
echo "Migration completed successfully." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment