Skip to main content

How to Attach a Virtual Disk to a Hyper-V VM in Batch Script

Virtual hard disks (VHDs and VHDXs) are the storage containers for Hyper-V virtual machines. Adding new disks to a VM is a routine task for expanding storage capacity, attaching data drives, mounting database volumes, or connecting pre-built template disks. Automating this process through Batch Script ensures consistent disk configuration across VMs and integrates seamlessly into provisioning workflows.

In this guide, we will explore how to create and attach virtual disks to Hyper-V virtual machines from a Batch Script using PowerShell cmdlets.

Understanding Disk Controllers

Hyper-V VMs have two types of disk controllers:

ControllerGeneration 1Generation 2Boot SupportHot-Add
IDEYes (2 channels, 2 disks each)NoYes (Gen 1 only)No
SCSIYes (up to 64 disks per controller)YesYes (Gen 2 only)Yes

Recommendation: Always use SCSI controllers. They support hot-add (attaching while the VM is running), have no 4-disk limit, and are the only option for Generation 2 VMs.

Method 1: Creating and Attaching a New Disk

@echo off
setlocal

:: CONFIGURATION - Update these for your environment
set "vm_name=WebServer-01"
set "vhd_path=C:\Hyper-V\VHDs\%vm_name%_Data.vhdx"
set "vhd_size_gb=10"

:: Verify Admin
net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Administrator privileges required.
pause
exit /b 1
)

:: Ensure the destination directory exists
if not exist "C:\Hyper-V\VHDs" mkdir "C:\Hyper-V\VHDs"

echo Creating and attaching a %vhd_size_gb% GB disk to "%vm_name%"...

:: Step 1: Create the VHDX
:: We must explicitly Import-Module Hyper-V as some systems don't auto-load it
powershell -NoProfile -Command "Import-Module Hyper-V; New-VHD -Path '%vhd_path%' -SizeBytes %vhd_size_gb%GB -Dynamic | Out-Null"

if %errorlevel% neq 0 (
echo [ERROR] Failed to create VHDX. Ensure Hyper-V is enabled and 'Hyper-V Module for Windows PowerShell' is installed.
pause
exit /b 1
)
echo [OK] VHDX created: %vhd_path%

:: Step 2: Attach to the VM
powershell -NoProfile -Command "Import-Module Hyper-V; Add-VMHardDiskDrive -VMName '%vm_name%' -Path '%vhd_path%' -ControllerType SCSI"

if %errorlevel%==0 (
echo [SUCCESS] Disk attached to %vm_name%.
echo The guest OS will see a new uninitialized disk.
) else (
echo [ERROR] Failed to attach. Ensure the VM "%vm_name%" actually exists.
)

pause

Dynamic vs. Fixed Disks

TypeCreated WithBehaviorBest For
Dynamic-DynamicStarts small, grows as data is writtenGeneral use, saving host space
Fixed-FixedAllocates full size immediatelyMaximum I/O performance
Differencing-Differencing -ParentPathStores only changes from a parent diskLab templates, testing

Method 2: Attaching an Existing Disk

To attach a pre-existing VHDX file (e.g., a data disk from another VM):

@echo off
setlocal

set "vm_name=DatabaseVM"
set "vhd_path=D:\Hyper-V\VHDs\SharedData.vhdx"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

:: Verify the VHDX exists
if not exist "%vhd_path%" (
echo [ERROR] VHDX not found: %vhd_path%
pause
exit /b 1
)

echo Attaching existing disk to "%vm_name%"...

powershell -NoProfile -Command ^
"Add-VMHardDiskDrive -VMName '%vm_name%' -Path '%vhd_path%' -ControllerType SCSI"

if %errorlevel%==0 (
echo [SUCCESS] Disk attached.
) else (
echo [ERROR] Failed. The disk may already be attached to another VM.
)

pause
warning

A VHDX file can only be attached to one running VM at a time (unless using Shared VHDX for clustering). Attempting to attach a disk that is already in use by another VM will fail.

Method 3: Adding Multiple Data Disks

For VMs that need several data volumes (e.g., database servers with separate disks for data, logs, and tempdb):

@echo off
setlocal enabledelayedexpansion

set "vm_name=SQLServer-01"
set "vhd_base=D:\Hyper-V\VHDs"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

echo Creating storage layout for "%vm_name%"...
echo.

:: Define disks: name|size_gb
set "disk_count=4"
set "disks[0]=Data|200"
set "disks[1]=Logs|50"
set "disks[2]=TempDB|30"
set "disks[3]=Backup|500"

set /a last=disk_count - 1
for /L %%i in (0,1,!last!) do (
for /f "tokens=1,2 delims=|" %%A in ("!disks[%%i]!") do (
set "disk_name=%%A"
set "disk_size=%%B"
set "disk_path=!vhd_base!\%vm_name%_%%A.vhdx"

echo Creating %%A drive (%%B GB^)...

powershell -NoProfile -Command ^
"New-VHD -Path '!disk_path!' -SizeBytes %%BGB -Dynamic | Out-Null;" ^
"Add-VMHardDiskDrive -VMName '%vm_name%' -Path '!disk_path!' -ControllerType SCSI"

if !errorlevel!==0 (
echo [OK] !disk_path!
) else (
echo [FAIL] !disk_path!
)
)
)

echo.
echo [DONE] Disk layout:
powershell -NoProfile -Command ^
"(Get-VM -Name '%vm_name%').HardDrives |" ^
"Format-Table ControllerType, ControllerNumber, ControllerLocation, Path -AutoSize"

pause

Method 4: Hot-Adding a Disk to a Running VM

SCSI disks can be added while the VM is running. The guest OS detects the new disk immediately.

@echo off
setlocal

set "vm_name=WebServer-01"
set "vhd_path=D:\Hyper-V\VHDs\%vm_name%_Extra.vhdx"
set "vhd_size_gb=50"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

:: Verify VM is running
set "state="
for /f "delims=" %%S in ('powershell -NoProfile -Command ^
"(Get-VM -Name '%vm_name%' -ErrorAction SilentlyContinue).State"') do set "state=%%S"

if not defined state (
echo [ERROR] VM "%vm_name%" not found.
pause
exit /b 1
)

if /i not "%state%"=="Running" (
echo [ERROR] VM must be Running for hot-add. Current state: %state%
pause
exit /b 1
)

echo Hot-adding %vhd_size_gb% GB disk to running VM "%vm_name%"...

:: Create the VHDX
powershell -NoProfile -Command ^
"New-VHD -Path '%vhd_path%' -SizeBytes %vhd_size_gb%GB -Dynamic | Out-Null"

if %errorlevel% neq 0 (
echo [ERROR] Failed to create VHDX.
pause
exit /b 1
)

:: Attach to running VM
powershell -NoProfile -Command ^
"Add-VMHardDiskDrive -VMName '%vm_name%' -Path '%vhd_path%' -ControllerType SCSI"

if %errorlevel%==0 (
echo [SUCCESS] Disk hot-added. The guest OS should detect it immediately.
echo Initialize and format the disk inside the VM using Disk Management.
) else (
echo [ERROR] Hot-add failed.
)

pause
tip

After hot-adding a disk, you need to initialize, partition, and format it inside the guest OS. On Windows guests, open Disk Management (diskmgmt.msc) or use diskpart. On Linux guests, use lsblk, fdisk, and mkfs.

Method 5: Listing All Disks Attached to a VM

@echo off
setlocal

set "vm_name=SQLServer-01"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

echo Disks attached to "%vm_name%":
echo ================================
echo.

powershell -NoProfile -Command ^
"foreach ($hd in (Get-VM -Name '%vm_name%').HardDrives) {" ^
" $vhd = Get-VHD -Path $hd.Path -ErrorAction SilentlyContinue;" ^
" $file = Get-Item $hd.Path -ErrorAction SilentlyContinue;" ^
" $maxGB = if ($vhd) { [math]::Round($vhd.Size / 1GB, 1) } else { 'N/A' };" ^
" $fileGB = if ($file) { [math]::Round($file.Length / 1GB, 2) } else { 'N/A' };" ^
" Write-Host (' [{0}:{1}:{2}] {3}' -f $hd.ControllerType, $hd.ControllerNumber, $hd.ControllerLocation, $hd.Path);" ^
" Write-Host (' Max Size: {0} GB, Current File: {1} GB' -f $maxGB, $fileGB);" ^
" Write-Host ''" ^
"}"

pause

Removing a Disk from a VM

@echo off
setlocal

set "vm_name=WebServer-01"
set "vhd_path=D:\Hyper-V\VHDs\WebServer-01_Extra.vhdx"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

echo Detaching disk from "%vm_name%"...

powershell -NoProfile -Command ^
"$drive = Get-VMHardDiskDrive -VMName '%vm_name%' | Where-Object Path -eq '%vhd_path%';" ^
"if ($drive) { Remove-VMHardDiskDrive -VMHardDiskDrive $drive; Write-Host '[OK] Disk detached.' }" ^
"else { Write-Host '[ERROR] Disk not found on this VM.'; exit 1 }"

if %errorlevel%==0 (
echo The VHDX file is preserved on disk.
) else (
echo Detach failed.
)

pause

Common Mistakes

The Wrong Way: Using IDE for Data Disks

:: WRONG - IDE has a 4-disk limit and no hot-add support
powershell -Command "Add-VMHardDiskDrive -VMName 'MyVM' -Path 'disk.vhdx' -ControllerType IDE"

Output Concern: IDE controllers are limited to 2 channels with 2 devices each (4 total). They do not support hot-add, meaning the VM must be stopped to add or remove disks. Always use SCSI controllers for data disks.

The Wrong Way: Attaching a Disk Used by Another VM

:: WRONG - VHDX is already mounted by another running VM
powershell -Command "Add-VMHardDiskDrive -VMName 'VM-B' -Path 'D:\VHDs\in-use-by-VM-A.vhdx'"

Hyper-V enforces exclusive access to VHDX files. Attaching a disk that is already in use by a running VM will fail. Detach it from the first VM before attaching to another.

Best Practices

  1. Always use SCSI controllers: They support hot-add, have no device limits, and are required for Gen 2 VMs.
  2. Use dynamic disks by default: Save host storage with dynamically expanding VHDXs unless maximum I/O is required.
  3. Separate data from OS disks: Use dedicated VHDXs for application data, databases, and logs.
  4. Name VHDXs descriptively: Include the VM name and purpose (e.g., SQLServer-01_Data.vhdx).
  5. Initialize disks inside the guest: After attaching, the guest OS must partition and format the new disk.

Conclusion

Attaching virtual disks to Hyper-V VMs from a Batch Script is handled by PowerShell's New-VHD and Add-VMHardDiskDrive cmdlets.

By creating purposefully named VHDX files, attaching them to SCSI controllers, and leveraging hot-add for running VMs, administrators can dynamically expand storage capacity without downtime.

Combining disk creation with multi-volume provisioning scripts enables standardized storage layouts for database servers, file servers, and any workload requiring structured disk configurations.