Migrating a GCE Persistent Disk Between Projects

Posted: five months and fifteen days ago

If you have a Google Cloud account with access to multiple projects and you want to move a GCE persistent disk between them, there is no direct method of doing so. Thankfully though, there is a roundabout way. It involves turning the disk into an image, then creating a new disk from said image in the new project. There's also a small gotcha in that the disk can't be mounted when you do this. Unless of course you were to snapshot the disk first, then you'd get a near perfect clone of the disk in your new project with it still being mounted in the old one.

Here's a script to move a disk from your current project to some other project you have access to, it assumes you are staying in the same zone:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/bin/bash
# This script will migrate a GCE Persistent Disk between two GCP projects.
# It moves the disk from your current context gcloud project, to the specified
# one by name/ID. If the disk is not detached a temp one will be made first.

function verify_args () {
  if [[ "x$1" != "x" && "x$2" != "x" ]]; then
    export GCE_DISK=$1
    export NEW_PROJECT=$2
  else
    echo "-- available disks --"
    gcloud compute disks list | tail -n +2 | awk '{print $1}'
    echo "--"
    echo "usage: $0 <gce-persistent-disk> <to-project-name>"
    exit 1
  fi

  export SNAPSHOT=
  export OLD_PROJECT=$(gcloud config list core/project | grep project | cut -d '=' -f2 | sed 's/ //g')

  # ensure the other project exists and we have access
  gcloud config set core/project $NEW_PROJECT
  gcloud compute disks list > /dev/null
  VALID_PROJECT=$?
  gcloud config set core/project $OLD_PROJECT
  if [ $VALID_PROJECT != 0 ]; then
    echo "destination project $NEW_PROJECT is invalid"
    exit 1
  fi
}

function confirm_disk () {
  # ensure the disk exists and has no users, or make a new one
  MOUNTED=$(gcloud compute disks describe $GCE_DISK | grep users:)
  gcloud compute disks list | grep $GCE_DISK > /dev/null
  if [ $? != 0 ]; then
    echo "could not find disk $GCE_DISK in project $OLD_PROJECT"
    exit 1
  elif [[ "x$MOUNTED" != "x" ]]; then
    export SNAPSHOT="1"
    echo "disk $GCE_DISK is mounted in $OLD_PROJECT, creating a temporary disk first"
    gcloud compute disks snapshot $GCE_DISK --snapshot-names $GCE_DISK-$NEW_PROJECT > /dev/null || exit 1
    gcloud compute disks create $GCE_DISK-$NEW_PROJECT --source-snapshot $GCE_DISK-$NEW_PROJECT > /dev/null || exit 1
    yes | gcloud compute snapshots delete $GCE_DISK-$NEW_PROJECT > /dev/null || exit 1
  fi
}

function migrate_disk () {
  # turn the disk into an image and pull it across, clean up temp resources
  if [ $SNAPSHOT ]; then
    SOURCE_DISK=$GCE_DISK-$NEW_PROJECT
  else
    SOURCE_DISK=$GCE_DISK
  fi

  gcloud compute images create $GCE_DISK-$NEW_PROJECT --source-disk $SOURCE_DISK > /dev/null || exit 1
  gcloud config set core/project $NEW_PROJECT
  gcloud compute disks create $GCE_DISK --image=/$OLD_PROJECT/$GCE_DISK-$NEW_PROJECT
  CREATED=$?
  gcloud config set core/project $OLD_PROJECT
  yes | gcloud compute images delete $GCE_DISK-$NEW_PROJECT || exit 1

  if [ $SNAPSHOT ]; then
    yes | gcloud compute disks delete $GCE_DISK-$NEW_PROJECT || exit 1
  fi

  exit $CREATED
}

verify_args $*
confirm_disk
migrate_disk