Skip to content

Instantly share code, notes, and snippets.

@quangnhut123
Last active June 26, 2024 07:37
Show Gist options
  • Save quangnhut123/ad7082e5c60fa4be0eb1f7161aecb7c7 to your computer and use it in GitHub Desktop.
Save quangnhut123/ad7082e5c60fa4be0eb1f7161aecb7c7 to your computer and use it in GitHub Desktop.
#!/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