guideonce.
← All guides

AWS · Migration · Windows Guests

Importing VMware and Hyper-V Disks into AWS to Build a Working EC2 Instance

Taking the disks out of a VMware or Hyper-V virtual machine and turning them into a booting EC2 instance. The two import paths, the bootable-versus-storage distinction that trips everyone up, and the driver work that decides whether the thing boots at all.

There are plenty of reasons to lift a virtual machine out of an on-premises hypervisor and land it in AWS: a datacentre exit, retiring ageing VMware or Hyper-V hosts, moving a single legacy workload to the cloud without rebuilding it, or just getting a stubborn appliance off hardware you no longer want to maintain. AWS has a tool for exactly this, VM Import/Export, and it works well once you understand two things it does not make obvious: that there are two quite different import paths, and that a bootable system disk needs handling completely differently from a plain data disk. This guide covers both, with a bias toward the disk-by-disk snapshot path because that is the one that gives you the most control when a machine has several disks.

The whole exercise is Windows-guest focused, because that is where the fiddly bits live (Disk Manager, VMware Tools, the driver situation). The same mechanics apply to Linux guests, but the driver and boot considerations are different and mostly easier, so where something is Windows-specific I say so.

The Two Import Paths

AWS gives you two mechanisms, and picking the right one up front saves a lot of backtracking.

import-image

Point AWS at a whole VM (an OVA, or a set of disks) and it produces a finished AMI directly, doing the boot-volume detection and conversion for you.

Best when: the VM is a single disk, or a clean multi-disk VM exported as one OVA, and you are happy to let AWS decide which disk is bootable.

import-snapshot

Import each disk individually as an EBS snapshot, then assemble an AMI yourself by nominating the boot snapshot and attaching the data snapshots as block device mappings.

Best when: the machine has several disks, the disks came out separately (for example from a V2V conversion tool), or you want explicit control over which disk boots and how the others attach.

This guide leads with the import-snapshot path because it is the one that handles a multi-disk machine cleanly and gives you room to fix problems disk by disk. The import-image path is covered afterward and is genuinely simpler when your VM is straightforward. Both rely on the same prerequisites and the same disk-format and driver preparation, so read the next three sections regardless of which path you choose.

Prerequisites: the vmimport Role and S3

VM Import/Export reads your disks from an S3 bucket and needs a specific IAM role called vmimport with a trust policy that lets the VM Import service assume it. This role is a one-time setup per account.

Trust policy (trust-policy.json)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "vmie.amazonaws.com" },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": { "sts:Externalid": "vmimport" }
      }
    }
  ]
}
Role permissions (role-policy.json), scoped to your bucket
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [ "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket" ],
      "Resource": [
        "arn:aws:s3:::<bucket-name>",
        "arn:aws:s3:::<bucket-name>/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:ModifySnapshotAttribute", "ec2:CopySnapshot",
        "ec2:RegisterImage", "ec2:Describe*"
      ],
      "Resource": "*"
    }
  ]
}
Create the role
aws iam create-role --role-name vmimport \
    --assume-role-policy-document "file://trust-policy.json"

aws iam put-role-policy --role-name vmimport \
    --policy-name vmimport \
    --policy-document "file://role-policy.json"

Then upload your prepared disks to the bucket. Each disk is a separate object:

aws s3 cp <boot-disk>.vhd  s3://<bucket-name>/
aws s3 cp <data-disk-1>.vhd s3://<bucket-name>/
aws s3 cp <data-disk-2>.vhd s3://<bucket-name>/

Getting the Disks into the Right Format

VM Import/Export accepts VHD/VHDX (the Hyper-V formats), VMDK (the VMware format), and raw. In practice you will be starting from one of two places, and you have a few ways to get to an acceptable format.

What you are starting from

Three ways to get a clean disk

Option 1: StarWind V2V Converter. This is a free tool that reads VMDK, VHD, VHDX, and raw, and writes any of them back out, including the AWS-friendly formats. It is the most flexible option when your source disks are in an awkward variant, because it re-writes the whole disk into a clean target format rather than trying to adjust the existing one. It also handles the VMware-to-Hyper-V-format conversion in a single pass, which is useful if you would rather hand AWS a VHD than a VMDK. In a recent migration this was the tool I reached for, converting each VM disk to a clean VHD before upload.

Option 2: Native Windows tooling. If your disks are already VHDX and you just need VHD, Hyper-V's own PowerShell does it without any third-party tool:

Convert VHDX to VHD
Convert-VHD -Path <source>.vhdx -DestinationPath <target>.vhd -VHDType Dynamic

And if you are capturing from a still-running physical or virtual Windows machine rather than exporting existing disk files, Microsoft's Disk2VHD (a Sysinternals tool) writes a VHD or VHDX directly from a live volume. Note the important detail with Disk2VHD: untick "Use Vhdx" if you want a VHD, and untick "Use volume Shadow Copy" only if you understand the consistency trade-off.

Option 3: AWS-side conversion. If you can get the disk into S3 at all in a supported format, you can let the import process itself do the heavy lifting, and if you need to change disk types afterward you can do that on the EBS volume once it exists. This is the least hands-on option but gives you the least control over the intermediate format.

Match the format to the path

Whichever tool you use, aim for one clean disk file per disk, in VHD or VMDK, with a sensible name you can track. When a machine has several disks you will be importing each one separately and then assembling them, so knowing which file is the boot disk and which are data disks matters. Name them so you can tell at a glance.

Bootable Versus Storage Disks

This is the part that catches people, and it is worth slowing down for. A virtual machine with several disks has exactly one that the firmware boots from (the one with the system partition and the bootloader) and one or more that are just storage. AWS treats these completely differently, and if you get the roles mixed up you get an AMI that either will not boot or boots but is missing its data.

Why they must be handled separately

When you import disks individually as snapshots, each snapshot is just a block-for-block copy of that one disk. Nothing about a data-disk snapshot tells AWS it is a data disk, and nothing about a boot-disk snapshot automatically makes it bootable in the resulting AMI. You establish which is which at assembly time, by nominating the boot snapshot as the root device and attaching the others as additional block devices. If you nominate the wrong disk as root, the instance will not boot; if you forget a data disk, the instance boots but the application on it cannot find its data.

Presenting and preparing disks individually

Before you even export the disks, it pays to have looked at the source machine's Disk Manager and written down the layout: which disk holds the operating system and boot partition, which disks hold data, what drive letters they map to, and roughly how big each is. This matters because after import you will be reconstructing that same layout in the EC2 instance, and if you attached the data disks to the source VM one at a time and brought them online individually in Disk Manager, you want to reproduce that same deliberate ordering rather than guessing.

The clean approach for a multi-disk Windows machine:

  1. In the source VM, open Disk Manager and record the disk number, partition layout, drive letter, and size of every disk. Screenshot it.
  2. Identify unambiguously which single disk is the boot/system disk. On a typical Windows machine this is the disk carrying both the small System Reserved partition and the C: volume.
  3. Export or convert each disk to its own file, named so the boot disk is obvious (for example a clear boot or os in the filename versus data for the others).
  4. Upload each disk file to S3 as a separate object.
A note on where data disks end up

When you assemble the AMI, the boot disk becomes the root volume and the data disks become additional volumes that will present inside Windows much as they did on the source machine. You may still need to go into Disk Manager on the running EC2 instance and bring the data volumes online and confirm their drive letters, exactly as you would after moving disks between physical machines. Presenting them one at a time and confirming each before moving to the next is the calm way to do it.

Driver Preparation and the Timing Decision

An EC2 instance is not a VMware or Hyper-V virtual machine, and the guest operating system needs the right drivers to boot and to reach the network in its new home. Two things are going on here, and the second one involves a genuine judgement call about timing.

Removing the old virtualisation tooling

A Windows guest that has been running under VMware will have VMware Tools installed; one from Hyper-V will have the Integration Services and their drivers. These install paravirtual storage and network drivers specific to that hypervisor. Left in place, they do nothing useful on EC2 and can occasionally interfere, so the general guidance is to remove VMware Tools (or the Hyper-V Integration components) from the guest as part of preparing it.

Adding what EC2 needs

EC2 instances use specific drivers for storage and networking: NVMe for EBS volumes on current instance types, and ENA (Elastic Network Adapter) for networking. A Windows guest that has never run on EC2 may not have these drivers present, and if the boot disk cannot present its own storage or bring up a network interface, you get an instance that either fails to boot or boots blind with no network. For Windows this is usually addressed by ensuring the AWS PV drivers, the NVMe driver, and the ENA driver are available to the guest before or during import.

The timing decision: before the image, or after restore

Here is the judgement call, and it is a real decision rather than a fixed rule. You can strip the old virtualisation drivers before you capture the disk image, or you can leave them in and deal with the driver swap after the disk is restored into EC2. Which is right depends on one question: does the source machine still need that virtualisation software present in order to boot far enough to be imaged in the first place?

The mistake to avoid is treating it as a rule in either direction. Some machines are perfectly happy to have their VMware Tools removed before capture; others fall over the moment you do. Check whether the source still boots without the tooling, and let that answer decide the order.

Do not strip a driver the source needs to boot

If removing VMware Tools or the Hyper-V Integration Services would stop the source machine seeing its boot disk, do not remove them before capture. You will be left with an image that was unbootable before it ever reached AWS. When in doubt, capture with the tooling in place and do the driver work after the disk is imported.

How clean does the source really need to be

There is a whole spectrum here, and where you sit on it depends on how much post-migration cleanup you are willing to do. One school of thought says the machine must be pristine before it goes anywhere near AWS: strip everything hypervisor-specific, uninstall old agents, remove anything that was bound to the old environment, then capture. That produces the cleanest imported instance and the least surprise afterward. It is also the most work up front, and if the migration turns out to be more of a proof-of-concept than a final production move, a lot of that work is wasted.

The other school (which is often the more practical one) is to accept some post-migration cleanup on the EC2 side in exchange for a much lighter prep on the source. Leave most things in place, take the image, boot it in EC2, and then work through the leftover tooling one thing at a time on the running instance. The result is the same in the end, and the total effort is often lower, because you only clean up the things that turn out to actually cause problems rather than pre-emptively removing everything that might. For a test run in particular, where you just want to see whether the machine even boots on EC2 before committing to the migration, the removal of virtualisation tooling and old drivers can often be skipped entirely: you are not trying to produce a supportable production instance, you are trying to prove viability.

The one thing worth removing before you take the image, regardless of which school you are in, is anything that binds itself to the machine's security identifier (SID) or otherwise ties itself to the physical or virtual host identity. The obvious culprits are endpoint security agents: antivirus and EDR products typically register the machine to a management console keyed to the SID, and the imported instance either shows up in the console as a duplicate of the source (confusing and hard to untangle) or refuses to communicate correctly. Backup agents, monitoring agents, and some licence-server clients have the same problem. The clean answer is to uninstall these agents before capture, and reinstall them fresh on the EC2 instance once it is running. Leaving them in and cleaning up afterward is technically possible but is genuinely awkward with these specific categories of tool, because their management consoles do not forgive the collision cleanly.

So, in short: for a production migration, more prep pays off in less pain afterward; for a test or proof-of-concept run, minimal prep is often fine and you can decide what actually needs fixing based on what actually breaks. Either way, the SID-bound tooling is the specific thing to deal with up front, because it is the one category that is meaningfully harder to fix on the EC2 side than on the source.

Path A: Import Each Disk as a Snapshot, Then Assemble

This is the path that gives you the most control for a multi-disk machine. You import each disk as its own EBS snapshot, wait for all of them to complete, then register an AMI that nominates the boot snapshot as root and attaches the data snapshots.

Step 1: import each disk as a snapshot

You can pass the disk container inline, but for anything with several disks a JSON file per disk is cleaner. The container names the format and the S3 location of one disk.

containers file, one per disk (boot-disk.json shown)
{
  "Description": "<machine-name> boot disk",
  "Format": "vhd",
  "UserBucket": {
    "S3Bucket": "<bucket-name>",
    "S3Key": "<boot-disk>.vhd"
  }
}
Import each disk (run once per disk)
aws ec2 import-snapshot \
    --description "<machine-name> boot disk" \
    --disk-container "file://boot-disk.json"

aws ec2 import-snapshot \
    --description "<machine-name> data disk 1" \
    --disk-container "file://data-disk-1.json"

Each call returns an ImportTaskId like import-snap-0123456789abcdef0. Note each one down against which disk it is.

Step 2: watch the imports to completion

Monitor progress
# All in-flight snapshot import tasks
aws ec2 describe-import-snapshot-tasks

# A specific task
aws ec2 describe-import-snapshot-tasks \
    --import-task-ids <import-snap-id>

Each task moves through active (with a percentage) to completed. When a task completes, its output carries the resulting SnapshotId (like snap-0123456789abcdef0). Collect the snapshot ID for every disk before moving on. Large disks take a while; this is normal.

Step 3: register the AMI from the snapshots

This is the assembly step, and it is where the bootable-versus-storage distinction becomes concrete. The boot snapshot is mapped to the root device; each data snapshot is mapped to its own additional device.

Register the image
aws ec2 register-image \
    --name "<ami-name>" \
    --description "<machine-name> imported from on-prem" \
    --architecture x86_64 \
    --root-device-name "/dev/sda1" \
    --virtualization-type hvm \
    --ena-support \
    --block-device-mappings \
      "DeviceName=/dev/sda1,Ebs={SnapshotId=<boot-snap-id>,DeleteOnTermination=true,VolumeType=gp3}" \
      "DeviceName=xvdb,Ebs={SnapshotId=<data-snap-1-id>,DeleteOnTermination=false,VolumeType=gp3}" \
      "DeviceName=xvdc,Ebs={SnapshotId=<data-snap-2-id>,DeleteOnTermination=false,VolumeType=gp3}"

The key points in that command:

UEFI machines need a boot mode and a platform

If the source machine boots via UEFI rather than legacy BIOS, registering an AMI from snapshots requires you to say so, with --boot-mode uefi and a --platform value for the operating system. Miss this on a UEFI guest and it will not boot. A BIOS-booting guest does not need these.

register-image returns an AMI ID (ami-0123456789abcdef0). You can now launch an instance from it.

Path B: Import the Whole VM as an Image

When the machine is straightforward (a single disk, or a clean OVA export), the import-image path is less work because AWS does the boot detection and AMI creation for you.

Single-disk import
aws ec2 import-image \
    --description "<machine-name>" \
    --license-type BYOL \
    --disk-containers \
      "Format=vhd,UserBucket={S3Bucket=<bucket-name>,S3Key=<disk>.vhd}"

For a multi-disk VM on this path, you list multiple disk containers in the one command and AWS works out which is bootable. That is the convenience and also the limitation: you are trusting its detection rather than nominating the boot disk yourself, which is exactly why the snapshot path is preferable when the layout is at all unusual.

Monitor the image import
aws ec2 describe-import-image-tasks \
    --import-task-ids <import-ami-id>

On completion the task output carries the finished AMI ID directly, no separate register step needed.

License type matters for Windows

The --license-type flag controls whether AWS applies its own Windows licensing or you bring your own. Specify BYOL to retain your existing licence, or AWS to have AWS licence the operating system. Leaving it undefined applies a default that depends on the operating system, which is not always what you want, so set it explicitly.

First Boot and the Admin Password

Once you have an AMI (from either path), launch an instance from it. Pick an instance type that matches the guest's expectations, put it in a subnet you can reach, and attach a security group that lets you in (RDP for Windows, on 3389, from a controlled source).

The first boot of an imported Windows guest has one predictable wrinkle: the local administrator password. The imported machine keeps whatever local accounts and passwords it had on-premises, but the mechanisms EC2 normally uses to hand you a password (the EC2Launch or EC2Config agent generating one and encrypting it against your key pair) may not be in play on a machine that was never sysprepped for EC2. So you often cannot simply retrieve the password the way you would with a native AWS Windows AMI.

AWS documents the ways through this, and the route depends on what agent is present on the guest. The essential reference is the EC2 documentation on resetting a lost or unknown Windows administrator password, which covers the EC2Launch and EC2Config paths and the offline password-reset approach. The practical shape of it:

Reference

Follow AWS's own documented procedure for resetting a lost Windows administrator password (the "ResettingAdminPassword" guidance in the EC2 User Guide). It is the authoritative source for the EC2Launch versus EC2Config versus offline routes, and it changes often enough that you want the current version rather than a remembered one.

Troubleshooting

The instance will not boot (no status checks pass)

Almost always one of two things: the wrong disk was nominated as root, or the boot disk is missing the storage driver it needs on EC2. Check first that /dev/sda1 really is the boot snapshot. If it is, this is the driver situation: the guest cannot present its own boot volume because the NVMe or PV driver is not there. Use the EC2 serial console or the detach-and-inspect approach to confirm, and inject the drivers via AWS's documented recovery route.

The instance boots but has no network

The ENA driver is missing or ENA support was not declared on the AMI. Confirm the guest has the ENA driver, and that you registered the AMI with --ena-support. A guest with no network but a passing boot check is the classic signature of this.

The instance boots but a data disk is missing

Either a data snapshot was left out of the block device mappings, or the volume is present but offline inside Windows. Check the block device mappings on the AMI first; if the volume is attached, open Disk Manager on the instance and bring it online, then confirm its drive letter.

The import task fails outright

Read the StatusMessage in the describe-import output. The common causes are a disk format the importer will not accept (convert it, per the format section), a vmimport role that is missing or lacks access to the bucket, or a disk that is corrupt or was uploaded incompletely. The status message usually names which.

A UEFI guest will not boot despite a correct root disk

You likely imported it without declaring the boot mode. A UEFI machine needs --boot-mode uefi and a --platform at registration; without them AWS assumes legacy BIOS and the guest will not come up.


Quick-Action Checklist

The whole flow in order. Each step links back to the detail above.

  1. Record the source disk layout from the VM's Disk Manager: which disk boots, which are data, drive letters, sizes. Screenshot it.
  2. Decide the driver timing. Will the source still boot without VMware Tools / Integration Services? If yes, strip before capture. If no, leave them in and fix on the EC2 side after import.
  3. Decide how much source-side cleanup to do. For a production migration, strip virtualisation tooling and old agents before capture where you safely can. For a test or proof-of-concept run, minimal prep is often fine. Either way, uninstall SID-bound tooling (antivirus, EDR, backup agents, monitoring agents) before capture: they bind to the machine's identity and cause console-side collisions if you clone them into EC2. Reinstall them fresh on the instance once it is running.
  4. Ensure EC2 drivers are catered for: NVMe and ENA for the Windows guest, before or during import.
  5. Convert each disk to a clean VHD or VMDK using StarWind V2V, native Windows tooling, or AWS-side conversion. One file per disk, clearly named.
  6. Set up the vmimport role (once per account) and upload each disk to S3 as a separate object.
  7. Choose your path. Multi-disk or awkward layout, use import-snapshot. Simple single-disk or clean OVA, use import-image.
  8. On the snapshot path: import each disk with import-snapshot, watch each to completed with describe-import-snapshot-tasks, and collect every SnapshotId.
  9. Assemble the AMI with register-image: boot snapshot on /dev/sda1, data snapshots on xvdb onward, --ena-support if the driver is present, and --boot-mode uefi plus --platform if the guest is UEFI.
  10. Launch and get in. Use the machine's existing on-prem local admin credentials, or follow AWS's documented admin-password reset if you cannot retrieve one.
  11. Bring data disks online in Disk Manager on the running instance and confirm drive letters, one at a time.

Closing Thoughts

The mechanics of VM Import/Export are not complicated once you separate the two ideas the tooling blurs together: importing a disk is just copying blocks, and making an instance boot is a separate act of assembly where you decide which disk is the system disk and make sure it has the drivers to stand up in its new home. Get the disk layout written down before you start, make the driver-timing decision deliberately rather than by habit, and keep the boot disk and the data disks clearly separated in your head and your filenames, and the rest is mostly waiting for imports to finish. The payoff is a workload lifted off hardware you no longer want, running on infrastructure you no longer have to maintain.

← All guides