Article on Medium: https://medium.com/@palladiusbonton/how-to-migrate-projects-across-organizations-c7e254ab90af
This gist: https://gist.github.com/palladius/a99993feb7e6d78b7a2abea0a10c3242
You could add this to a .envrc
and use direnv
for instance:
$ cat .envrc
# Your Org Admin account, and gcloud already working for it:
export POWER_ACCOUNT='[email protected]'
# SRC ORG: 12345678901 source.example.com
export SRC_ORG_ID="12345678901"
export SRC_ORG_DOMAIN='source.example.com'
# DST ORG: 45678901234 destination.example.com
export DST_ORG_ID="45678901234"
export DST_ORG_DOMAIN='destination.example.com'
make sure to change it and source
it every time you change it (or use direnv, again)
$ gcloud resource-manager folders list --folder '133363080569'
DISPLAY_NAME PARENT_NAME ID
palladius-eu folders/133363080569 1038774614299
goliardia-mygbiz-com folders/133363080569 1060878125337
ricc-rocks folders/133363080569 232459007745
palladius-it folders/133363080569 240430455062
carlessos-org folders/133363080569 412566726466
palladi-us folders/133363080569 681226422412
SPECIAL_ORPHANS folders/133363080569 737267668168
sredemo-dev folders/133363080569 883877833337
gcloud organizations get-iam-policy "$ORG_ID" \
-flatten='bindings[].members' \
-format='table(bindings.role,bindings.members)' |
tee "t.org-iam-policy.$ORG_DOMAIN.txt"
- Asset_inventory_command (complex, tree structure):
gcloud beta asset search-all-resources \
--asset-types=cloudresourcemanager.googleapis.com/Project \
--scope=organizations/$ORGID | egrep '^name:' | cut -d/ -f 5
- Simplified_single_command:
gcloud projects list --filter "parent.id=$ORGID AND parent.type=organization" | \
awk '{print $1 }' | grep -v PROJECT_ID
- Super duper graphical way:
git clone https://github.com/palladius/org-folder-projects-graph/
./recurse_folders.rb my.domain.com # before move
make cache-clean # after move
./recurse_folders.rb my.domain.com # after move
gcloud projects update "$MY_PROJECT_ID" --name='My New Description max 30chars'
gcloud resource-manager org-policies allow --organization "$SRC_ORG_ID" \
resourcemanager.allowedExportDestinations \
"under:organizations/$DST_ORG_ID"
gcloud resource-manager org-policies allow --organization "$DST_ORG_ID" \
resourcemanager.allowedImportSource \
"under:organizations/$SRC_ORG_ID"
# in case you need to add [email protected] to domain1.com
# You might want to give [email protected] access to Source Org
# or viceversa to [email protected] access to Destination Org.
# more info: https://cloud.google.com/resource-manager/docs/organization-policy/restricting-domains
$ gcloud resource-manager org-policies allow \
--organization "$SRC_ORG_ID" \
iam.allowedPolicyMemberDomains 'C0abcdefg' # gives access to SrcOrg to Dest id
# Set IAM for SrcOrg
$ gcloud organizations add-iam-policy-binding $ORG_ID \
--member='user:$ACCOUNT' \
--role='roles/resourcemanager.organizationAdmin' \
--role='roles/resourcemanager.projectIamAdmin' \
--role='roles/resourcemanager.projectMover' \
--role='roles/orgpolicy.policyAdmin' \
--condition=None
Orphans are account-dependent. To cacth them all, you might want to run a simple iteration like this:
for ACCOUNT in [email protected] [email protected] ; do
gcloud --account "$ACCOUNT" projects list --filter="parent.id.yesno(yes='Yes', no='No')=No" | tee "out/16listorphans-for-$ACCOUNT.txt"
done
It's important you dump this list to file so you can then re-use it and give it as source to another script.
For instance you can take this output above and recurse into a gcloud command:
cat out/[email protected] | \
awk '{print $1}' | \
while read PROJECT_ID ; do
# Note you need to do `--organization` if its a root folder.
echo gcloud beta projects move "$PROJECT_ID" --folder "$DESTINATION_ID" ;
done | tee migrate.sh