Skip to main content

Migrate EBS Volumes to a New AWS Account

Move EBS volumes from an external AWS account into a SleakOps-managed account by creating snapshots, sharing them, copying to the destination, and recreating the volumes.

Prerequisites

  • AWS CLI configured with profiles for both source and destination accounts
  • kubectl access to the source cluster to identify volume IDs
  • The destination account ID (12-digit format)
  • A SleakOps Project deployed in the destination account (to attach the IAM policy)

Let's Start

Step 1 — Identify the volume IDs to migrate

Run this command against the source cluster to list all pods and their attached Persistent Volumes:

kubectl get pods --all-namespaces -o json | jq -c '.items[] |
{
namespace: .metadata.namespace,
pod: .metadata.name,
pvc: (.spec.volumes[]? | select(.persistentVolumeClaim != null) | .persistentVolumeClaim.claimName)
} | select(.pvc != null)'

Build a list of pod names and their associated volume IDs:

pod_volume_ids=(
"core-stg-postgresql-0 vol-025d6ddd6af9df250"
"core-stg-redis-master-0 vol-0a36b5ff1809fcc49"
"grafana-6dfd99b599-26gz9 vol-0f851cc80d49463b5"
)

Step 2 — Create and share snapshots (source account)

This script creates a snapshot for each volume and shares it with the destination account. Replace target_account_id with the destination AWS account ID:

pod_volume_ids=(
"pod_name1 vol_id1"
"pod_name2 vol_id2"
)
target_account_id="111222333444"

for entry in "${pod_volume_ids[@]}"; do
pod_name=$(echo "$entry" | awk '{print $1}')
volume_id=$(echo "$entry" | awk '{print $2}')
snapshot_name="Snapshot-$volume_id"

snapshot_id=$(aws ec2 create-snapshot \
--volume-id "$volume_id" \
--description "Snapshot of $volume_id from Pod $pod_name" \
--tag-specifications "ResourceType=snapshot,Tags=[{Key=Name,Value=$snapshot_name},{Key=Pod,Value=$pod_name},{Key=DestinationAccount,Value=$target_account_id}]" \
--query SnapshotId --output text)

echo "Waiting for $snapshot_id to complete..."
aws ec2 wait snapshot-completed --snapshot-ids "$snapshot_id"

aws ec2 modify-snapshot-attribute \
--snapshot-id "$snapshot_id" \
--attribute createVolumePermission \
--operation-type add \
--user-ids "$target_account_id"

echo "Snapshot $snapshot_id shared with account $target_account_id"
done

Step 3 — Attach the required IAM policy (destination account)

Attach this policy to the IAM role used by a Pod in your destination SleakOps Project. The role name follows the pattern {PROJECT_NAME}-{SHORT_UUID}-sa:

{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ec2:DescribeSnapshots",
"ec2:CopySnapshot",
"ec2:CreateVolume",
"ec2:CreateTags",
"ec2:DescribeVolumes",
"ec2:DescribeSnapshotAttribute",
"ec2:DeleteSnapshot",
"ec2:DeleteVolume"
],
"Effect": "Allow",
"Resource": "*"
}
]
}

Step 4 — Copy snapshots and create volumes (destination account)

Run this script from a Pod in the destination account that has the policy from Step 3. Replace the regions and availability zone as needed:

source_region="us-east-1"
destination_region="us-east-1"
availability_zone="us-east-1a"

pod_volume_ids=(
"pod_name1 vol_id1"
"pod_name2 vol_id2"
)

for entry in "${pod_volume_ids[@]}"; do
pod_name=$(echo "$entry" | awk '{print $1}')
volume_id=$(echo "$entry" | awk '{print $2}')
snapshot_id=$(aws ec2 describe-snapshots \
--restorable-by-user-ids self \
--filters "Name=tag:Name,Values=Snapshot-$volume_id" "Name=tag:Pod,Values=$pod_name" \
--query "Snapshots[0].SnapshotId" \
--output text \
--region "$source_region")

new_snapshot_id=$(aws ec2 copy-snapshot \
--source-region "$source_region" \
--source-snapshot-id "$snapshot_id" \
--description "Copy of Snapshot-$volume_id from Pod $pod_name" \
--tag-specifications "ResourceType=snapshot,Tags=[{Key=Name,Value=Copied-$volume_id},{Key=Pod,Value=$pod_name},{Key=OriginalVolumeId,Value=$volume_id}]" \
--query SnapshotId --output text \
--region "$destination_region")

echo "Waiting for $new_snapshot_id to complete..."
aws ec2 wait snapshot-completed --snapshot-ids "$new_snapshot_id" --region "$destination_region"

new_volume_id=$(aws ec2 create-volume \
--snapshot-id "$new_snapshot_id" \
--availability-zone "$availability_zone" \
--volume-type gp3 \
--tag-specifications "ResourceType=volume,Tags=[{Key=Name,Value=Volume-$volume_id},{Key=OriginalVolumeId,Value=$volume_id},{Key=Pod,Value=$pod_name}]" \
--query VolumeId --output text \
--region "$destination_region")

aws ec2 wait volume-available --volume-ids "$new_volume_id" --region "$destination_region"
echo "New Volume ID: $new_volume_id (Original: $volume_id)"
done
warning

Record the new volume IDs and their availability zones. Workloads must be deployed in the same AZ as their volume.