Skip to content

Instantly share code, notes, and snippets.

@mikesparr
Last active March 26, 2025 16:51
Show Gist options
  • Select an option

  • Save mikesparr/63a453407443c72e33ce6b526df492de to your computer and use it in GitHub Desktop.

Select an option

Save mikesparr/63a453407443c72e33ce6b526df492de to your computer and use it in GitHub Desktop.
Example gcloud commands to migrate a Google Cloud Project from organization to organization
#!/usr/bin/env bash
#####################################################################
# REFERENCES
# - https://cloud.google.com/resource-manager/docs/project-migration
# - https://olivelink.net/blog/migrating-google-cloud-projects-between-organisations-a-step-by-step-guide (console)
#####################################################################
# vars
export SOURCE_ORG="<SOURCE-ORG-ID>" # get from `gcloud organizations list`
export DESTINATION_ORG="<DEST-ORG-ID>" # get from `gcloud organizations list`
export AUTH_USER="someone@someco.com" # who you auth to perform migrate
export MOVE_PROJECT_ID="your-example-project-id"
#####################################################################
# SOURCE ORG
#
# Org Admin or user with:
# - Organization Policy Administrator
# - Folder Administrator
# - Project Mover
#####################################################################
export EXPORT_FOLDER_NAME="export-to-destination"
# create folder
gcloud resource-manager folders create --display-name=$EXPORT_FOLDER_NAME --organization=$SOURCE_ORG
# get folder ID
export EXPORT_FOLDER_ID=$(gcloud resource-manager folders list \
--organization $SOURCE_ORG --filter="displayName:$EXPORT_FOLDER_NAME" --format="value(ID)")
# move project into folder
gcloud beta projects move $MOVE_PROJECT_ID --folder $EXPORT_FOLDER_ID
# grant IAM policies on folder
gcloud resource-manager folders add-iam-policy-binding $EXPORT_FOLDER_ID \
--member="user:$AUTH_USER" --role="roles/resourcemanager.projectIamAdmin"
gcloud resource-manager folders add-iam-policy-binding $EXPORT_FOLDER_ID \
--member="user:$AUTH_USER" --role="roles/resourcemanager.projectMover"
# create org policy to allow export to destination org
export MIGRATE_POLICY_FILE=policy-alloweddestinations.yaml
cat > $MIGRATE_POLICY_FILE << EOF
constraint: constraints/resourcemanager.allowedExportDestinations
listPolicy:
allowed_values:
- under:organizations/$DESTINATION_ORG
EOF
gcloud beta resource-manager org-policies set-policy $MIGRATE_POLICY_FILE --folder=$EXPORT_FOLDER_ID
#####################################################################
# DESTINATION ORG
#
# Org Admin or user with:
# - Organization Policy Administrator
# - Folder Administrator
# - Project Mover
#####################################################################
export IMPORT_FOLDER_NAME="import-from-source"
# create folder
gcloud resource-manager folders create --display-name=$IMPORT_FOLDER_NAME --organization=$DESTINATION_ORG
# get folder ID
export IMPORT_FOLDER_ID=$(gcloud resource-manager folders list \
--organization $DESTINATION_ORG --filter="displayName:$IMPORT_FOLDER_NAME" --format="value(ID)")
# grant IAM policies on folder
gcloud resource-manager folders add-iam-policy-binding $IMPORT_FOLDER_ID \
--member="user:$AUTH_USER" --role="roles/resourcemanager.projectIamAdmin"
gcloud resource-manager folders add-iam-policy-binding $IMPORT_FOLDER_ID \
--member="user:$AUTH_USER" --role="roles/resourcemanager.projectMover"
# create org policy to allow export to playground
export IMPORT_POLICY_FILE=policy-allowedsources.yaml
cat > $IMPORT_POLICY_FILE << EOF
constraint: constraints/resourcemanager.allowedImportSources
listPolicy:
allowed_values:
- under:organizations/$SOURCE_ORG
EOF
gcloud beta resource-manager org-policies set-policy $IMPORT_POLICY_FILE --folder=$IMPORT_FOLDER_ID
#####################################################################
# AUTHORIZED USER
# REF: https://cloud.google.com/resource-manager/docs/project-migration#assign_permissions
#
# User with folder permissions (assigned above):
# - Project Mover
# - Project IAM Administrator
# - Optional: Project Billing Manager, Billing User
# - Optional: Project Creator (if moving to org level instead of folder)
#####################################################################
# move project into folder (logged in as auth user)
gcloud beta projects move $MOVE_PROJECT_ID --folder $IMPORT_FOLDER_ID
# repeat as necessary for each project
#####################################################################
# CLEAN UP (SOURCE ORG)
# REF: https://cloud.google.com/resource-manager/docs/project-migration#import_export_folders
#####################################################################
# delete migration folder (to remove temp permissions)
gcloud resource-manager folders delete $EXPORT_FOLDER_ID
#####################################################################
# CLEAN UP (DESTINATION ORG)
# REF: https://cloud.google.com/resource-manager/docs/project-migration#import_export_folders
#####################################################################
# move project(s) out of folder and wherever you decide thereafter
gcloud beta projects move $MOVE_PROJECT_ID [--folder <SOME OTHER FOLDER ID> | --organization $DESTINATION_ORG_ID]
# delete migration folder (to remove temp permissions)
gcloud resource-manager folders delete $IMPORT_FOLDER_ID
@mikesparr

mikesparr commented Sep 16, 2022

Copy link
Copy Markdown
Author

Google Cloud Platform Overview

gcp-structure-001

Google Cloud Platform (GCP) consists of one or more Projects. All Google Cloud Platform resources (i.e. - virtual machines, storage, databases, managed services) reside within a Project.

Projects can have any number of users associated to them, each with different permissions assigned (grouped in roles). Users are identified by their email address which can be either you@gmail.com or you can upgrade your Google mail account to a Workspace plan to use a custom domain you@example.com.

When you create a Project on GCP, your email address is automatically assigned as the first user and you are given the "Owner" role. You can then add more users and assign them as "Owner" or any other role. The "account" is the Project and the "users" are just email addresses optionally assigned to it.

GCP has an internal mechanism that links the Project to a custom domain name if it exists, they call an "Organization", which is nothing more than a categorization. If a Project is created with an @gmail.com account, it doesn't create this link and has a status of "No Organization". If this link exists, you can optionally create permissions for one or more projects in groups instead of one at a time.

Unfortunately Google does not make it possible to "un-link" a custom domain from a Project without linking it to another one. Their documentation ( https://cloud.google.com/resource-manager/docs/project-migration ) instead requires you to transfer the Project from "org" A to "org" B, which is really just pointing the group policies to a different domain name (the name "organization" is simply a logical concept and is really just a categorization).

In order to fully remove access for someone from custom domain A, you need to link the projects to custom domain B. This is how GCP works. Only on special circumstances and with a premium support plan costing approximately $12K/mo, can you request GCP to unlink domains and set them back to the "No Organization" status, so the fastest and easiest way is to just link them to another domain to sever the relationship to the old one.

Commands above simplify this project transfer process

The steps included in this Gist simplify what Google and others have documented with point-and-click steps in the cloud console, but instead leverages the gcloud sdk commands.

If you prefer point-and-click in the Cloud Console, then this article below also shows the step-by-step process.

Cloud Resource Hierarchy Explained

Cloud resource hierarchy diagram

As with other cloud platforms, the primary "asset" on GCP is the Project (similar to an Account on AWS), and the groupings are purely optional pointers, or categorizations. This is described in the article and diagram above.

Google Cloud Platform Resource Hierarchy

GCP assets all reside in Projects, however you can optionally organize your projects in groups if you point them to a custom domain name (upgrading your basic email plan to a Workspace plan, or signing up for Cloud Identity plan). An "organization" is not an actual account, just an optional group Google generates, and it points it to a domain name. It is not an actual thing, plan, or account and merely a categorization.

As noted above, if you want to group your Projects somewhere else, you have to point them to the new domain, and that requires running the commands in Google's documentation above, or the gcloud commands shared in this Gist.

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