Skip to content

How to Create a Hyper-V Virtual Switch in Batch Script

Learn how to automate the creation of Hyper-V virtual switches (external, internal, and private) using PowerShell cmdlets in a Batch script for VM networking.

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.

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```


<Aside type="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.

</Aside>


## 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

  1. Use descriptive names: Name switches based on their purpose (e.g., Production-External, Lab-Isolated).
  2. Keep -AllowManagementOS $true: Unless you have a dedicated management NIC, always allow the host to use the external switch.
  3. Use NAT for controlled internet: NAT switches give VMs internet access without exposing them directly to the physical network.
  4. Check for connected VMs before deleting: Warn administrators about VMs that will lose connectivity.
  5. 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.

Navigation