How to Create a Hyper-V Virtual Machine from Batch Script
Provisioning new virtual machines is one of the most frequent tasks in virtualized environments. While the Hyper-V Manager GUI is convenient for creating one or two VMs, automating the process via Batch Script is essential for lab setups, deployment pipelines, infrastructure-as-code workflows, and rapid environment provisioning where dozens of VMs need to be created with consistent configurations.
In this guide, we will explore how to create Hyper-V virtual machines from a Batch Script using PowerShell's New-VM cmdlet, including configuring CPU, memory, networking, and virtual hard disks.
Method 1: Creating a Basic VM
The minimum required parameters for New-VM are a name and a memory allocation.
@echo off
setlocal
:: 1. Hardened Environment
set "PATH=%SystemRoot%\System32;%SystemRoot%;%SystemRoot%\System32\WindowsPowerShell\v1.0"
:: 2. Verify Administrator Privileges
%SystemRoot%\System32\fltmc.exe >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Administrator privileges required. Right-click and Run as Admin.
pause & exit /b 1
)
:: 3. Configuration
set "vm_name=TestVM-01"
set "memory_mb=2048"
set "vm_path=C:\Hyper-V\VMs"
echo.
echo ==================================================
echo HYPER-V VM PROVISIONING: %vm_name%
echo ==================================================
echo [INFO] Memory: %memory_mb% MB
echo [INFO] Path: %vm_path%
echo.
:: 4. PowerShell Bridge (Secured via $env: variables)
:: - Forces Import of Hyper-V module
:: - Ensures the target directory exists
:: - Creates a Generation 2 Virtual Machine
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command ^
"try { Import-Module Hyper-V -ErrorAction Stop } catch { throw 'The Hyper-V PowerShell module is not installed or enabled on this system. (Requires Windows Pro/Ent)' }; " ^
"if (-not (Test-Path -LiteralPath $env:vm_path)) { New-Item -ItemType Directory -Path $env:vm_path -Force | Out-Null }; " ^
"New-VM -Name $env:vm_name -MemoryStartupBytes ([long]$env:memory_mb * 1MB) -Path $env:vm_path -Generation 2 -ErrorAction Stop"
if %errorlevel% equ 0 (
echo.
echo [SUCCESS] Virtual Machine "%vm_name%" created successfully.
) else (
echo.
echo [ERROR] Failed to create VM. Check if Hyper-V is enabled and name is unique.
)
pause
endlocal
Generation 1 vs. Generation 2
| Feature | Generation 1 | Generation 2 |
|---|---|---|
| Boot firmware | BIOS | UEFI |
| Secure Boot | No | Yes |
| Boot from VHDX | Yes | Yes |
| Boot from SCSI | No | Yes |
| Max disk size | 2 TB (VHD) | 64 TB (VHDX) |
| Linux support | Full | Most modern distros |
Recommendation: Use Generation 2 for all new VMs unless you need legacy OS support (Windows XP, Server 2003, older Linux).
Method 2: Creating a Fully Configured VM
A production VM needs CPU, memory, a virtual hard disk, a network adapter, and an ISO for OS installation.
@echo off
setlocal enabledelayedexpansion
:: VM Configuration
set "vm_name=WebServer-Prod"
set "vm_path=D:\Hyper-V\VMs"
set "vhd_path=D:\Hyper-V\VHDs\%vm_name%.vhdx"
set "vhd_size_gb=80"
set "memory_gb=4"
set "cpu_count=4"
set "switch_name=External-Switch"
set "iso_path=D:\ISOs\WindowsServer2022.iso"
:: Check for admin privileges
net session >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Admin required. Right-click and "Run as administrator".
pause
exit /b 1
)
:: Check Hyper-V is available
powershell -NoProfile -Command "Get-Command New-VM" >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Hyper-V module not available. Is Hyper-V installed?
pause
exit /b 1
)
echo =============================================
echo CREATING VM: %vm_name%
echo =============================================
echo.
set "step_failed=0"
:: Step 1: Create the VM
echo [1/5] Creating VM...
powershell -NoProfile -Command ^
"try { New-VM -Name '%vm_name%' -MemoryStartupBytes %memory_gb%GB -Path '%vm_path%' -Generation 2 -NoVHD -ErrorAction Stop | Out-Null; exit 0 } catch { Write-Host $_.Exception.Message; exit 1 }"
if !errorlevel! neq 0 (
echo [ERROR] Failed to create VM.
pause
exit /b 1
)
echo VM created.
:: Step 2: Create and attach the virtual hard disk
echo [2/5] Creating %vhd_size_gb%GB VHDX...
powershell -NoProfile -Command ^
"try { New-VHD -Path '%vhd_path%' -SizeBytes %vhd_size_gb%GB -Dynamic -ErrorAction Stop | Out-Null; Add-VMHardDiskDrive -VMName '%vm_name%' -Path '%vhd_path%' -ErrorAction Stop; exit 0 } catch { Write-Host $_.Exception.Message; exit 1 }"
if !errorlevel! neq 0 (
echo [ERROR] Failed to create or attach VHDX.
set "step_failed=1"
) else (
echo VHDX attached.
)
:: Step 3: Set CPU count
echo [3/5] Setting %cpu_count% vCPUs...
powershell -NoProfile -Command ^
"try { Set-VMProcessor -VMName '%vm_name%' -Count %cpu_count% -ErrorAction Stop; exit 0 } catch { Write-Host $_.Exception.Message; exit 1 }"
if !errorlevel! neq 0 (
echo [ERROR] Failed to set CPU count.
set "step_failed=1"
) else (
echo CPUs configured.
)
:: Step 4: Connect network adapter
echo [4/5] Connecting to switch "%switch_name%"...
powershell -NoProfile -Command ^
"try { Connect-VMNetworkAdapter -VMName '%vm_name%' -SwitchName '%switch_name%' -ErrorAction Stop; exit 0 } catch { Write-Host $_.Exception.Message; exit 1 }"
if !errorlevel! neq 0 (
echo [ERROR] Failed to connect network. Verify switch "%switch_name%" exists.
set "step_failed=1"
) else (
echo Network connected.
)
:: Step 5: Mount ISO for installation
echo [5/5] Mounting ISO...
if not exist "%iso_path%" (
echo [WARNING] ISO not found: %iso_path%
echo Skipping ISO mount. You can attach one later.
set "step_failed=1"
) else (
powershell -NoProfile -Command ^
"try { Add-VMDvdDrive -VMName '%vm_name%' -Path '%iso_path%' -ErrorAction Stop; exit 0 } catch { Write-Host $_.Exception.Message; exit 1 }"
if !errorlevel! neq 0 (
echo [ERROR] Failed to mount ISO.
set "step_failed=1"
) else (
echo ISO mounted.
)
)
echo.
if !step_failed! equ 0 (
echo =============================================
echo VM CREATED SUCCESSFULLY
echo =============================================
) else (
echo =============================================
echo VM CREATED WITH WARNINGS [review above]
echo =============================================
)
echo.
echo Name: %vm_name%
echo Memory: %memory_gb% GB
echo CPUs: %cpu_count%
echo Disk: %vhd_size_gb% GB ^(%vhd_path%^)
echo Network: %switch_name%
echo ISO: %iso_path%
echo.
echo Start the VM with: powershell -Command "Start-VM -Name '%vm_name%'"
pause
endlocal
exit /b 0
Method 3: Creating a VM with Dynamic Memory
Dynamic memory allows Hyper-V to adjust the VM's memory allocation based on actual demand.
@echo off
setlocal enabledelayedexpansion
set "vm_name=DynamicMemVM"
set "vm_path=C:\Hyper-V\VMs"
set "mem_startup=2GB"
set "mem_min=512MB"
set "mem_max=8GB"
:: Check admin privileges
net session >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Admin required. Right-click and "Run as administrator".
pause
exit /b 1
)
:: Check Hyper-V is available
powershell -NoProfile -Command "Get-Command New-VM -ErrorAction Stop" >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Hyper-V module not available. Is Hyper-V installed?
pause
exit /b 1
)
echo =============================================
echo Creating VM: %vm_name%
echo =============================================
echo.
:: Step 1: Create the VM
echo [1/2] Creating VM...
powershell -NoProfile -Command ^
"try { New-VM -Name '%vm_name%' -MemoryStartupBytes %mem_startup% -Path '%vm_path%' -Generation 2 -NoVHD -ErrorAction Stop | Out-Null; exit 0 } catch { Write-Host $_.Exception.Message; exit 1 }"
if !errorlevel! neq 0 (
echo [ERROR] Failed to create VM.
pause
exit /b 1
)
echo VM created.
:: Step 2: Configure dynamic memory
echo [2/2] Configuring dynamic memory ^(%mem_min% - %mem_max%^)...
powershell -NoProfile -Command ^
"try { Set-VMMemory -VMName '%vm_name%' -DynamicMemoryEnabled $true -MinimumBytes %mem_min% -StartupBytes %mem_startup% -MaximumBytes %mem_max% -ErrorAction Stop; exit 0 } catch { Write-Host $_.Exception.Message; exit 1 }"
if !errorlevel! neq 0 (
echo [WARNING] VM created but dynamic memory configuration failed.
pause
exit /b 1
)
echo Dynamic memory configured.
echo.
echo =============================================
echo VM CREATED SUCCESSFULLY
echo =============================================
echo.
echo Name: %vm_name%
echo Startup: %mem_startup%
echo Range: %mem_min% - %mem_max%
echo Dynamic: Enabled
echo.
pause
endlocal
exit /b 0
| Parameter | Purpose |
|---|---|
MinimumBytes | Lowest memory allocation (floor) |
StartupBytes | Initial memory at boot |
MaximumBytes | Highest memory allocation (ceiling) |
Method 4: Bulk VM Creation
For lab environments or training setups that need multiple identical VMs:
@echo off
setlocal enabledelayedexpansion
set "base_name=LabVM"
set "count=5"
set "vm_path=D:\Hyper-V\Labs"
set "vhd_dir=D:\Hyper-V\VHDs"
set "switch_name=Internal-Lab"
set "mem_startup=2GB"
set "vhd_size=40GB"
set "cpu_count=2"
:: Check admin privileges
net session >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Admin required. Right-click and "Run as administrator".
pause
exit /b 1
)
:: Check Hyper-V is available
powershell -NoProfile -Command "Get-Command New-VM -ErrorAction Stop" >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Hyper-V module not available.
pause
exit /b 1
)
:: Verify virtual switch exists
powershell -NoProfile -Command ^
"try { Get-VMSwitch -Name '%switch_name%' -ErrorAction Stop | Out-Null; exit 0 } catch { exit 1 }"
if !errorlevel! neq 0 (
echo [ERROR] Virtual switch "%switch_name%" not found.
echo Create it first: New-VMSwitch -Name "%switch_name%" -SwitchType Internal
pause
exit /b 1
)
echo =============================================
echo BULK VM CREATION: %count% Lab VMs
echo =============================================
echo.
set "created=0"
set "failed=0"
for /L %%i in (1,1,%count%) do (
set "name=%base_name%-%%i"
set "vhd=%vhd_dir%\%base_name%-%%i.vhdx"
echo [%%i/%count%] Creating !name!...
powershell -NoProfile -Command ^
"try {" ^
" $ErrorActionPreference = 'Stop';" ^
" $vmName = '!name!';" ^
" $vhdPath = '!vhd!';" ^
" New-VM -Name $vmName -MemoryStartupBytes %mem_startup% -Path '%vm_path%' -Generation 2 -NoVHD | Out-Null;" ^
" New-VHD -Path $vhdPath -SizeBytes %vhd_size% -Dynamic | Out-Null;" ^
" Add-VMHardDiskDrive -VMName $vmName -Path $vhdPath;" ^
" Set-VMProcessor -VMName $vmName -Count %cpu_count%;" ^
" Connect-VMNetworkAdapter -VMName $vmName -SwitchName '%switch_name%';" ^
" exit 0" ^
"} catch {" ^
" Write-Host $_.Exception.Message;" ^
" exit 1" ^
"}"
if !errorlevel! equ 0 (
echo [OK] !name! created successfully.
set /a "created+=1"
) else (
echo [FAIL] !name! creation failed.
set /a "failed+=1"
)
echo.
)
echo =============================================
if !failed! equ 0 (
echo ALL %count% VMs CREATED SUCCESSFULLY
) else (
echo COMPLETED: !created! OK, !failed! FAILED
)
echo =============================================
echo.
:: Show summary table
powershell -NoProfile -Command ^
"Get-VM -Name '%base_name%*' 2>$null | Format-Table Name, State, @{N='Memory(GB)';E={$_.MemoryStartup/1GB}}, ProcessorCount -AutoSize"
pause
endlocal
exit /b 0
Method 5: Creating a VM from an Existing Template VHDX
Instead of installing an OS from scratch, copy a pre-configured VHDX (sysprepped template) and attach it to a new VM.
@echo off
setlocal
set "vm_name=ClonedServer"
set "template_vhd=D:\Templates\Win2022-Sysprepped.vhdx"
set "new_vhd=D:\Hyper-V\VHDs\%vm_name%.vhdx"
set "vm_path=D:\Hyper-V\VMs"
set "switch_name=External-Switch"
net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)
:: Verify template exists
if not exist "%template_vhd%" (
echo [ERROR] Template VHDX not found: %template_vhd%
pause
exit /b 1
)
:: Step 1: Copy the template VHDX
echo Copying template disk...
copy "%template_vhd%" "%new_vhd%" >nul
if %errorlevel% neq 0 (
echo [ERROR] Failed to copy template VHDX.
pause
exit /b 1
)
echo [OK] Template cloned.
:: Step 2: Create the VM using the cloned disk
echo Creating VM...
powershell -NoProfile -Command ^
"New-VM -Name '%vm_name%' -MemoryStartupBytes 4GB -Path '%vm_path%' -Generation 2 -VHDPath '%new_vhd%';" ^
"Set-VMProcessor -VMName '%vm_name%' -Count 4;" ^
"Connect-VMNetworkAdapter -VMName '%vm_name%' -SwitchName '%switch_name%'"
if %errorlevel% equ 0 (
echo [OK] VM ready. Start it to run through sysprep OOBE.
) else (
echo [ERROR] VM creation failed. The cloned VHDX may need to be cleaned up.
)
pause
endlocal
Using sysprepped template VHDXs is the fastest way to provision new Windows VMs. The OS is pre-installed and configured; only the initial setup (computer name, network, activation) runs on first boot.
Common Mistakes
The Wrong Way: Not Specifying a Generation
:: WRONG - Defaults to Generation 1, which uses legacy BIOS
powershell -Command "New-VM -Name 'MyVM' -MemoryStartupBytes 4GB"
Output Concern:
Without specifying -Generation 2, the VM defaults to Generation 1, which uses the legacy BIOS boot model. Modern operating systems benefit from UEFI, Secure Boot, and SCSI boot capabilities available only in Generation 2.
The Wrong Way: Using Fixed-Size Disks Unnecessarily
:: WRONG for most cases - Creates an 80GB file immediately
powershell -Command "New-VHD -Path 'disk.vhdx' -SizeBytes 80GB -Fixed"
Fixed-size VHDs allocate the full disk space upfront (80 GB file on host). Dynamic VHDs start small and grow as data is written, conserving host storage. Use fixed-size only when maximum I/O performance is required.
Best Practices
- Use Generation 2: Always specify
-Generation 2for modern operating systems. - Use dynamic VHDX: Save host disk space with dynamically expanding virtual hard disks.
- Enable dynamic memory: Let Hyper-V adjust RAM allocation based on demand.
- Store VMs on fast storage: Place VHDX files on SSDs or high-performance storage for best VM performance.
- Use templates: Clone sysprepped VHDXs instead of installing from ISO for rapid provisioning.
Conclusion
Creating Hyper-V virtual machines from a Batch Script is accomplished by wrapping PowerShell's New-VM and related cmdlets (New-VHD, Set-VMProcessor, Connect-VMNetworkAdapter). By parameterizing the VM configuration (name, memory, CPU, disk, network, and ISO), administrators can build reusable provisioning scripts that create consistent, production-ready VMs in seconds. Combining template-based cloning with bulk creation loops enables rapid environment provisioning for labs, development, and production deployments.