Skip to content

How to Create a Hyper-V Virtual Machine from Batch Script

Automate Hyper-V VM provisioning with Batch Scripts using PowerShell's New-VM cmdlet, including CPU, memory, networking, and VHDX configuration for labs, deployments, and infrastructure-as-code workflows.

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

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

  1. Use Generation 2: Always specify -Generation 2 for modern operating systems.
  2. Use dynamic VHDX: Save host disk space with dynamically expanding virtual hard disks.
  3. Enable dynamic memory: Let Hyper-V adjust RAM allocation based on demand.
  4. Store VMs on fast storage: Place VHDX files on SSDs or high-performance storage for best VM performance.
  5. 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.

Navigation