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:
| Controller | Generation 1 | Generation 2 | Boot Support | Hot-Add |
|---|---|---|---|---|
| IDE | Yes (2 channels, 2 disks each) | No | Yes (Gen 1 only) | No |
| SCSI | Yes (up to 64 disks per controller) | Yes | Yes (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
| Type | Created With | Behavior | Best For |
|---|---|---|---|
| Dynamic | -Dynamic | Starts small, grows as data is written | General use, saving host space |
| Fixed | -Fixed | Allocates full size immediately | Maximum I/O performance |
| Differencing | -Differencing -ParentPath | Stores only changes from a parent disk | Lab 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
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
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
- Always use SCSI controllers: They support hot-add, have no device limits, and are required for Gen 2 VMs.
- Use dynamic disks by default: Save host storage with dynamically expanding VHDXs unless maximum I/O is required.
- Separate data from OS disks: Use dedicated VHDXs for application data, databases, and logs.
- Name VHDXs descriptively: Include the VM name and purpose (e.g.,
SQLServer-01_Data.vhdx). - 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.