Last active
March 26, 2025 16:51
-
-
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
This file contains hidden or 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
| #!/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 |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Google Cloud Platform Overview
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 sdkcommands.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
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
gcloudcommands shared in this Gist.