,

Clone an EBS Volume to Another Account: Tutorial

Workbench render showing an EBS volume being cloned to another account under a second KMS key

Here’s the whole job, end to end: clone an EBS volume to another account, re-encrypt it under a key that account owns, attach it, and confirm the data survived the trip. No snapshots, no intermediate artifacts, no waiting for a copy job to crawl through the night. EBS Volume Clones across accounts made this a short walk, and this is that walk.

Our scenario: a production account (111122223333) holds a data volume in us-east-1. A development account (444455556666) wants a copy for testing, encrypted under its own key so prod can revoke access independently.

Prerequisites

  • Two AWS accounts you control, ideally in the same AWS Organization with AWS RAM sharing enabled for the org. Two named CLI profiles: prod and dev.
  • A source EBS volume encrypted with a customer managed KMS key. This is non-negotiable — volumes encrypted with the AWS-managed aws/ebs key cannot be shared across accounts. Check yours with aws ec2 describe-volumes and look at KmsKeyId.
  • Admin-ish rights in both accounts: EC2, KMS key policy editing, and RAM.
  • A running EC2 instance in the dev account that you can attach a volume to, in the same physical zone as the source volume. We’ll sort out what “same zone” means in step 1.
  • The region must be one where EBS Volume Clones is available — confirm in the EBS Volume Clones docs.

Clone an EBS volume to another account, step by step

  1. Find the source volume and, more importantly, its AZ ID. Availability Zone names map to different physical zones in different accounts. AZ IDs don’t. Get both.

    aws ec2 describe-volumes --profile prod --region us-east-1 \
      --volume-ids vol-0a1b2c3d4e5f67890 \
      --query 'Volumes[0].{AZ:AvailabilityZone,Size:Size,Key:KmsKeyId,State:State}'
    
    # Translate that AZ name into an AZ ID
    aws ec2 describe-availability-zones --profile prod --region us-east-1 \
      --query 'AvailabilityZones[].{Name:ZoneName,Id:ZoneId}' --output table

    Write down the ZoneId — something like use1-az1. That’s the value that means the same thing in both accounts. The AZ ID documentation explains why AWS shuffles the names.

  2. Create the destination key in the dev account. This is the key the clone will be re-encrypted under.

    aws kms create-key --profile dev --region us-east-1 \
      --description "Dev environment EBS clone key" \
      --query 'KeyMetadata.Arn' --output text
    
    aws kms create-alias --profile dev --region us-east-1 \
      --alias-name alias/dev-ebs-clones \
      --target-key-id arn:aws:kms:us-east-1:444455556666:key/REPLACE-WITH-KEY-ID
  3. Grant the dev account use of the source key. Pull the existing key policy first — you’re adding a statement, not replacing the document. Losing the root administration statement on a KMS key is a bad afternoon.

    aws kms get-key-policy --profile prod --region us-east-1 \
      --key-id alias/prod-data-key --policy-name default \
      --output text > key-policy.json
  4. Add the cross-account statement and put the policy back. Append this to the Statement array in key-policy.json, then upload it. Scope the principal to the specific dev role once it works; account root is fine for a first pass.

    {
      "Sid": "AllowDevAccountCloneDecrypt",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::444455556666:root" },
      "Action": ["kms:Decrypt", "kms:DescribeKey", "kms:CreateGrant"],
      "Resource": "*",
      "Condition": { "Bool": { "kms:GrantIsForAWSResource": "true" } }
    }

    Then: aws kms put-key-policy --profile prod --key-id alias/prod-data-key --policy-name default --policy file://key-policy.json. See the KMS cross-account key policy guide for the full pattern.

  5. Share the volume from the prod account via AWS RAM. In the console this is the “Share volume” action on the volume; from the CLI it’s a resource share. Tag it with an expiry so future-you knows when to revoke it.

    aws ram create-resource-share --profile prod --region us-east-1 \
      --name prod-data-vol-to-dev \
      --resource-arns arn:aws:ec2:us-east-1:111122223333:volume/vol-0a1b2c3d4e5f67890 \
      --principals 444455556666 \
      --tags key=Purpose,value=dev-refresh key=Expires,value=2026-10-07
Render of a cloned EBS volume being attached to an EC2 instance in the target account
  1. Accept the share in the dev account. If both accounts are in the same Organization with sharing enabled, this may already be done for you — the list will come back empty and the volume will simply be visible. Otherwise, accept the invitation.

    aws ram get-resource-share-invitations --profile dev --region us-east-1 \
      --query 'resourceShareInvitations[?status==`PENDING`].resourceShareInvitationArn' \
      --output text
    
    aws ram accept-resource-share-invitation --profile dev --region us-east-1 \
      --resource-share-invitation-arn arn:aws:ram:us-east-1:111122223333:resource-share-invitation/EXAMPLE-GUID
  2. Translate the AZ ID into a local AZ name. Same command as step 1, different account, and probably a different answer. Match on ZoneId, take the ZoneName.

    aws ec2 describe-availability-zones --profile dev --region us-east-1 \
      --query 'AvailabilityZones[?ZoneId==`use1-az1`].ZoneName' --output text

    If prod’s volume lives in us-east-1a and this returns us-east-1c, that’s not a bug — that’s the whole point of AZ IDs.

  3. Run the copy from the dev account, specifying the destination AZ name from step 7 and the dev key from step 2. Flag names for the copy operation vary by CLI version — treat this as illustrative and confirm against the EBS Volume Clones documentation.

    # Illustrative — verify the operation name and flags for your CLI version
    aws ec2 copy-volume --profile dev --region us-east-1 \
      --source-volume-id vol-0a1b2c3d4e5f67890 \
      --availability-zone us-east-1c \
      --encrypted \
      --kms-key-id alias/dev-ebs-clones \
      --tag-specifications 'ResourceType=volume,Tags=[{Key=Env,Value=dev},{Key=ClonedFrom,Value=prod},{Key=ExpiresOn,Value=2026-10-07}]'

    Tag at creation time. A cloned production volume with no tags is an unexplained line item waiting to happen.

  4. Wait for the clone to become available, then attach it. The new volume ID comes back from step 8.

    aws ec2 wait volume-available --profile dev --region us-east-1 \
      --volume-ids vol-0999888777666555a
    
    aws ec2 attach-volume --profile dev --region us-east-1 \
      --volume-id vol-0999888777666555a \
      --instance-id i-0abcdef1234567890 \
      --device /dev/sdf
  5. Mount it on the instance and verify the data. On the dev EC2 host — note we mount read-only first, because confirming the contents doesn’t require write access and you don’t want a stray process modifying a fresh clone before you’ve looked at it.

    lsblk
    sudo mkdir -p /mnt/clone
    sudo mount -o ro /dev/nvme1n1 /mnt/clone
    ls -la /mnt/clone
    df -h /mnt/clone
  6. Clean up when you’re done. Revoke the share so the grant doesn’t outlive its purpose, and delete the clone when the test cycle ends. Standard EBS charges run for as long as that volume exists.

    sudo umount /mnt/clone
    aws ec2 detach-volume --profile dev --volume-id vol-0999888777666555a
    aws ec2 wait volume-available --profile dev --volume-ids vol-0999888777666555a
    aws ec2 delete-volume --profile dev --volume-id vol-0999888777666555a
    
    # And in prod, retire the share when the refresh window closes
    aws ram delete-resource-share --profile prod \
      --resource-share-arn arn:aws:ram:us-east-1:111122223333:resource-share/EXAMPLE-GUID

What you built

A working cross-account refresh path: a production volume shared through RAM as a standing grant that costs nothing until used, a clone created on demand by the consuming account, re-encrypted on arrival under a key that account controls, attached and verified. Sharing is free; you paid a one-time fee based on volume size on the dev side when the copy was initiated, plus standard EBS charges while the clone lives.

The obvious next move is to stop doing this by hand. Every command above is an API call, so wrapping steps 5 through 9 in a Step Functions state machine on an EventBridge schedule turns a manual refresh into a Monday-morning job. Add a data-masking task between the clone and the handoff if the source holds anything sensitive, and a tag-driven sweeper that deletes clones past their ExpiresOn date. Every one of these steps lands in CloudTrail in both accounts, so the audit trail comes free — build the alert on unexpected share creation while it’s fresh in your mind.

At Enkompass (enkompass.net), this is our home turf. We design AWS architectures that survive contact with production — including the environment-refresh plumbing nobody puts on a roadmap until it breaks. If you’d rather have seasoned cloud people handle it instead of learning the hard way at 2 AM, let’s talk. Reach out through our contact page or call us directly at +1-828-436-5667 — we’ll help you turn this from a someday-project into a done-deal.

Ready when you are

Let’s map your route to AWS

A free 60-minute architecture review with a senior engineer. No slides, no SDR, just a plan.

Discover more from Enkompass

Subscribe now to keep reading and get access to the full archive.

Continue reading