How to Create a Hyper-V Virtual Switch in Batch Script
Virtual switches are the networking foundation of Hyper-V. Every virtual machine must connect to a virtual switch to communicate with other VMs, the host operating system, or the external network. Understanding and automating virtual switch creation is essential for building functional virtualized environments.
In this guide, we will explore how to create and configure the three types of Hyper-V virtual switches from a Batch Script using PowerShell cmdlets.
Understanding the Three Switch Types
| Type | VMs to VMs | VMs to Host | VMs to External Network | Use Case |
|---|---|---|---|---|
| External | Yes | Yes | Yes | Production VMs needing internet/LAN access |
| Internal | Yes | Yes | No | Host-to-VM communication without external access |
| Private | Yes | No | No | Isolated VM-only networks (labs, testing) |
Method 1: Creating an External Virtual Switch
An external switch binds to a physical network adapter on the host, giving VMs direct access to the physical network (and internet).
@echo off
setlocal enabledelayedexpansion
set "switch_name=External-Production"
:: Verify Admin
net session >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Administrator privileges required.
echo Right-click and "Run as administrator".
pause
exit /b 1
)
:: Check Hyper-V is available
powershell -NoProfile -Command "Get-Command New-VMSwitch -ErrorAction Stop" >nul 2>&1
if !errorlevel! neq 0 (
echo [ERROR] Hyper-V module not available. Is Hyper-V installed?
pause
exit /b 1
)
:: Check if switch already exists
powershell -NoProfile -Command ^
"try { Get-VMSwitch -Name '%switch_name%' -ErrorAction Stop | Out-Null; exit 0 } catch { exit 1 }"
if !errorlevel! equ 0 (
echo [ERROR] Switch "%switch_name%" already exists.
echo Remove it first: Remove-VMSwitch -Name "%switch_name%" -Force
pause
exit /b 1
)
:: List available physical adapters
echo =============================================
echo Available Physical Network Adapters
echo =============================================
powershell -NoProfile -Command ^
"Get-NetAdapter -Physical | Format-Table Name, Status, LinkSpeed, InterfaceDescription -AutoSize"
echo.
:: Get user input
set "adapter="
set /p "adapter=Enter the adapter name to bridge (e.g., Ethernet): "
if "!adapter!" == "" (
echo [ERROR] No adapter specified.
pause
exit /b 1
)
:: Validate the adapter exists and is up
powershell -NoProfile -Command ^
"try { $a = Get-NetAdapter -Name '!adapter!' -ErrorAction Stop; if ($a.Status -ne 'Up') { Write-Host '[WARNING] Adapter is not connected'; exit 2 }; exit 0 } catch { Write-Host '[ERROR] Adapter not found'; exit 1 }"
set "adapter_check=!errorlevel!"
if !adapter_check! equ 1 (
echo [ERROR] Adapter "!adapter!" does not exist. Check the name above.
pause
exit /b 1
)
if !adapter_check! equ 2 (
echo.
set /p "confirm=Adapter is down. Continue anyway? (Y/N): "
if /i not "!confirm!" == "Y" (
echo Cancelled.
pause
exit /b 0
)
)
echo.
echo Creating external switch "%switch_name%" on adapter "!adapter!"...
echo.
powershell -NoProfile -Command ^
"try {" ^
" New-VMSwitch -Name '%switch_name%' -NetAdapterName '!adapter!' -AllowManagementOS $true -ErrorAction Stop | Out-Null;" ^
" exit 0" ^
"} catch {" ^
" Write-Host $_.Exception.Message;" ^
" exit 1" ^
"}"
if !errorlevel! equ 0 (
echo =============================================
echo [SUCCESS] External switch created
echo =============================================
echo.
echo Switch: %switch_name%
echo Adapter: !adapter!
echo Management OS access: Enabled
echo.
echo VMs attached to this switch will have
echo full network access.
) else (
echo =============================================
echo [ERROR] Switch creation failed
echo =============================================
echo.
echo Common causes:
echo - Adapter already bound to another switch
echo - Adapter does not support SR-IOV/bridging
echo - Adapter name contains special characters
)
echo.
pause
endlocal
exit /b 0
The -AllowManagementOS Flag
| Value | Behavior |
|---|---|
$true (default) | The host OS can also use this adapter for its own networking. A virtual NIC is created for the host. |
$false | The host OS loses network access through this adapter. Only VMs can use it. |
Setting -AllowManagementOS $false will disconnect the host from the network through this adapter. If this is the only network adapter and you are connected remotely, you will lose your remote session. Always keep it $true unless you have a separate management NIC.
Method 2: Creating an Internal Virtual Switch
An internal switch enables communication between VMs and the host without exposing traffic to the physical network.
@echo off
setlocal
set "switch_name=Internal-Management"
net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)
echo Creating internal switch "%switch_name%"...
powershell -noprofile -command "New-VMSwitch -Name '%switch_name%' -SwitchType Internal"
if %errorlevel% equ 0 (
echo [SUCCESS] Internal switch created.
echo.
echo A virtual NIC has been created on the host.
echo Configure an IP address on it for host-to-VM communication:
echo Example: 192.168.100.1/24
) else (
echo [ERROR] Failed.
)
pause
Configuring the Host IP Address
After creating an internal switch, the host gets a new virtual adapter that needs an IP address:
@echo off
setlocal enabledelayedexpansion
:: 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. Run as Admin.
pause & exit /b 1
)
:: 3. Configuration
set "switch_name=Internal-Management"
set "ip=192.168.100.1"
set "prefix=24"
echo.
echo ==================================================
echo IP CONFIGURATION: %switch_name%
echo ==================================================
echo [INFO] Target IP: %ip%/%prefix%
echo.
:: 4. PowerShell Bridge (using $env: to prevent injection)
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command ^
"$adapter = Get-NetAdapter | Where-Object { $_.Name -like ('*' + $env:switch_name + '*') -or $_.InterfaceDescription -like ('*' + $env:switch_name + '*') } | Select-Object -First 1; " ^
"if ($adapter) { " ^
" try { " ^
" Remove-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -Confirm:$false -ErrorAction SilentlyContinue; " ^
" New-NetIPAddress -InterfaceIndex $adapter.ifIndex -IPAddress $env:ip -PrefixLength $env:prefix -ErrorAction Stop | Out-Null; " ^
" Write-Host ('[SUCCESS] IP ' + $env:ip + '/' + $env:prefix + ' assigned to ' + $adapter.Name); " ^
" exit 0; " ^
" } catch { " ^
" Write-Host ('[ERROR] ' + $_.Exception.Message); " ^
" exit 1; " ^
" } " ^
"} else { " ^
" Write-Host ('[ERROR] Virtual adapter for ' + $env:switch_name + ' not found.'); " ^
" exit 1; " ^
"}"
if !errorlevel! equ 0 (
echo.
echo The internal switch is now ready for communication.
) else (
echo.
echo [CRITICAL] IP Assignment failed.
)
pause
endlocal
exit /b 0
Method 3: Creating a Private Virtual Switch
A private switch creates a completely isolated network. Only VMs attached to this switch can communicate with each other. The host cannot access this network.
@echo off
setlocal enabledelayedexpansion
:: 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. Run as Admin.
pause & exit /b 1
)
set "switch_name=Private-Lab"
echo.
echo ==================================================
echo PROVISIONING PRIVATE SWITCH: %switch_name%
echo ==================================================
echo.
:: 3. PowerShell Bridge (Ensures module exists and creates switch)
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command ^
"try { Import-Module Hyper-V -ErrorAction Stop } catch { throw 'The Hyper-V module is not available.' }; " ^
"try { " ^
" New-VMSwitch -Name $env:switch_name -SwitchType Private -ErrorAction Stop | Out-Null; " ^
" exit 0; " ^
"} catch { " ^
" Write-Host ('[ERROR] ' + $_.Exception.Message); " ^
" exit 1; " ^
"}"
if !errorlevel! equ 0 (
echo.
echo [SUCCESS] Private switch "%switch_name%" created.
echo VMs attached to this switch are fully isolated.
echo No host or external network access.
) else (
echo.
echo [ERROR] Failed to create private switch.
)
pause
endlocal
exit /b 0```
:::info
Private switches are ideal for security testing, malware analysis labs, and any scenario where VMs must be completely air-gapped from the host and external networks.
:::
## Method 4: Complete Lab Network Setup
A practical script that creates a full lab environment with multiple switch types:
```batch
@echo off
setlocal enabledelayedexpansion
:: 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. Run as Admin.
pause & exit /b 1
)
echo.
echo ==================================================
echo LAB NETWORK SETUP: PROVISIONING
echo ==================================================
echo.
:: 3. PowerShell Provisioning Logic
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command ^
"try { Import-Module Hyper-V -ErrorAction Stop } catch { throw 'Hyper-V module missing.' }; " ^
" " ^
"Write-Host '[1/3] Creating Lab-Management (Internal)...'; " ^
"if (-not (Get-VMSwitch -Name 'Lab-Management' -ErrorAction SilentlyContinue)) { " ^
" New-VMSwitch -Name 'Lab-Management' -SwitchType Internal | Out-Null; Write-Host ' [OK] Created' " ^
"} else { Write-Host ' [SKIP] Already exists' }; " ^
" " ^
"Write-Host '[2/3] Creating Lab-Isolated (Private)...'; " ^
"if (-not (Get-VMSwitch -Name 'Lab-Isolated' -ErrorAction SilentlyContinue)) { " ^
" New-VMSwitch -Name 'Lab-Isolated' -SwitchType Private | Out-Null; Write-Host ' [OK] Created' " ^
"} else { Write-Host ' [SKIP] Already exists' }; " ^
" " ^
"Write-Host '[3/3] Creating Lab-NAT (Network Translation)...'; " ^
"if (-not (Get-VMSwitch -Name 'Lab-NAT' -ErrorAction SilentlyContinue)) { " ^
" New-VMSwitch -Name 'Lab-NAT' -SwitchType Internal -ErrorAction Stop | Out-Null; " ^
" $a = Get-NetAdapter | Where-Object { $_.Name -like '*Lab-NAT*' } | Select-Object -First 1; " ^
" New-NetIPAddress -InterfaceIndex $a.ifIndex -IPAddress 10.0.0.1 -PrefixLength 24 -ErrorAction Stop | Out-Null; " ^
" if (-not (Get-NetNat -Name 'LabNAT' -ErrorAction SilentlyContinue)) { " ^
" New-NetNat -Name 'LabNAT' -InternalIPInterfaceAddressPrefix '10.0.0.0/24' -ErrorAction Stop | Out-Null; " ^
" }; " ^
" Write-Host ' [OK] NAT network created (10.0.0.0/24, gateway 10.0.0.1)' " ^
"} else { Write-Host ' [SKIP] Already exists' }; " ^
" " ^
"Write-Host '`n============================================='; " ^
"Write-Host ' NETWORK SUMMARY'; " ^
"Write-Host '============================================='; " ^
"Get-VMSwitch | Format-Table Name, SwitchType, NetAdapterInterfaceDescription -AutoSize"
echo.
pause
endlocal
exit /b 0
Method 5: Listing and Deleting Virtual Switches
Listing All Switches
@echo off
setlocal
:: 1. Hardened Environment
set "PATH=%SystemRoot%\System32;%SystemRoot%;%SystemRoot%\System32\WindowsPowerShell\v1.0"
echo.
echo ==================================================
echo HYPER-V VIRTUAL SWITCHES
echo ==================================================
echo.
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command ^
"Get-VMSwitch | Format-Table Name, SwitchType, NetAdapterInterfaceDescription -AutoSize"
echo.
pause
Deleting a Switch
@echo off
setlocal enabledelayedexpansion
:: 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. Run as Admin.
pause & exit /b 1
)
:: 3. Configuration
set "switch_name=OldSwitch"
echo.
echo [INFO] Analyzing switch: %switch_name%
:: 4. PowerShell Analysis (Checks existence and connected VMs)
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command ^
"$s = Get-VMSwitch -Name $env:switch_name -ErrorAction SilentlyContinue; " ^
"if (-not $s) { exit 2 }; " ^
"$vms = @(Get-VMNetworkAdapter -All | Where-Object SwitchName -eq $env:switch_name); " ^
"if ($vms.Count -gt 0) { " ^
" Write-Host '`n[WARNING] The following VMs are connected to this switch:'; " ^
" $vms | Select-Object VMName -Unique | ForEach-Object { Write-Host (' - ' + $_.VMName) }; " ^
" Write-Host 'They will lose connectivity if you proceed.'; " ^
"}"
set "ps_status=!errorlevel!"
if !ps_status! equ 2 (
echo [INFO] Switch "%switch_name%" does not exist.
pause & exit /b 0
)
echo.
set /p "confirm=Are you sure you want to DELETE '%switch_name%'? (YES/NO): "
if /i not "!confirm!" == "YES" (
echo [CANCELLED] No changes were made.
pause & exit /b 0
)
:: 5. Delete Execution
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command ^
"Remove-VMSwitch -Name $env:switch_name -Force -ErrorAction Stop"
if !errorlevel! equ 0 (
echo [SUCCESS] Switch "%switch_name%" has been removed.
) else (
echo [ERROR] Failed to delete switch.
)
pause
endlocal
exit /b 0
Common Mistakes
The Wrong Way: Creating an External Switch on a Wireless Adapter
:: PROBLEMATIC - Wi-Fi adapters have limited support for external switches
powershell -command "New-VMSwitch -Name 'WiFi-Bridge' -NetAdapterName 'Wi-Fi'"
Output Concern: While Hyper-V can bind to wireless adapters, the functionality is limited. Many Wi-Fi drivers do not fully support the bridging capabilities required by external switches, leading to intermittent connectivity or complete failure. Use wired Ethernet adapters for external switches whenever possible.
The Wrong Way: Not Checking for Existing Switches
:: WRONG - Fails if the switch name already exists
powershell -command "New-VMSwitch -Name 'MySwitch' -SwitchType Internal"
If a switch with the same name already exists, the command fails. Always check for existence first with Get-VMSwitch -Name ... -ErrorAction SilentlyContinue.
Best Practices
- Use descriptive names: Name switches based on their purpose (e.g.,
Production-External,Lab-Isolated). - Keep
-AllowManagementOS $true: Unless you have a dedicated management NIC, always allow the host to use the external switch. - Use NAT for controlled internet: NAT switches give VMs internet access without exposing them directly to the physical network.
- Check for connected VMs before deleting: Warn administrators about VMs that will lose connectivity.
- Use private switches for security labs: Complete isolation prevents accidental network exposure of test environments.
Conclusion
Creating Hyper-V virtual switches from a Batch Script is accomplished through PowerShell's New-VMSwitch cmdlet with the -SwitchType parameter for internal and private switches, or -NetAdapterName for external switches. Understanding the three switch types and their connectivity characteristics is essential for designing proper virtual network architectures. By combining switch creation with IP address configuration and NAT rules, administrators can build complete virtual networking environments that serve everything from production hosting to isolated security testing labs.