Fixing Missing VMXNET3 Network Drivers in OSDCloud VDI Deployments

Deploying Windows with OSDCloud can be a huge time-saver, especially when combined with Autopilot for end-to-end provisioning. However, if you’ve worked with virtual desktop infrastructure (VDI) on VMware, you may have encountered a frustrating issue:

The deployment works fine in WinPE, but after reboot, the network card disappears – and Autopilot can’t continue.

We recently faced this exact scenario during a VDI rollout on a Broadcom platform. Here’s how we solved it.

The Scenario

  • Environment: VMware-based VDI hardware
  • Goal: Use OSDCloud for deployment with a user-driven Autopilot profile
  • Challenge: After the WinPE phase, Windows couldn’t detect the VMXNET3 network adapter. Without internet connectivity, Autopilot registration fails.

Initially, we tried to work around the issue by integrating VMware network drivers into the OSDCloud boot media.

First Attempt: Integrating Drivers into Boot Media

We used the built-in OSDCloud command to include VMware drivers:

Edit-OSDCloudWinPE -CloudDriver VMware

This worked during WinPE – but after the first reboot, Windows failed to load the VMXNET3 driver. Switching to the E1000 adapter type worked, but performance and feature limitations made it unsuitable for production.

Second Attempt: Pre-Injecting Drivers

We then tried this approach:

  1. Installed the latest VMware Tools on a standalone VM.
  2. Extracted only the network driver from VMware Tools.
  3. Injected it into WinPE and later applied it offline using DISM after OS installation.

Result: Same problem. After leaving WinPE, Windows still didn’t detect the VMXNET3 adapter.

Working Solution: On-the-Fly Driver Installation

The fix was to download and install the latest VMware Tools dynamically during the deployment process:

  • In WinPE phase: Download VMware Tools to local storage.
  • In specialize phase (first boot): Install the network driver package before Autopilot starts.

This ensures the VMXNET3 driver is present and active by the time Autopilot registration begins.

Implementation Details

Step 1: Download VMware Tools in WinPE

Add the following snippet to your WinPE OSDCloud script to fetch VMware Tools:

# Define the base URL for the latest VMware Tools directory
$baseUrl = "https://packages.vmware.com/tools/releases/latest/windows/x64/"

# Get the HTML content of the directory page
$htmlContent = Invoke-WebRequest -Uri $baseUrl -UseBasicParsing

# Parse the HTML to find the installer file
$installerFile = $htmlContent.Links | Where-Object { $_.href -match "VMware-tools-.*-x64.exe" } | Select-Object -First 1

if ($null -ne $installerFile) {
    $installerUrl = $baseUrl + $installerFile.href
    $toolsInstallerPath = "C:\OSDCloud\SW\VMware-tools-latest-x64.exe"
    
    # Download the installer
    Write-DarkGrayHost "Downloading the latest VMware Tools from $installerUrl"
    # Invoke-WebRequest -Uri $installerUrl -OutFile $toolsInstallerPath -UseBasicParsing
    curl.exe -L $installerUrl -o $toolsInstallerPath
}

Step 2: Edit your Untattend.xml file

Optional: download your Autopilot and VM scripts, which will be executed locally later.

Invoke-RestMethod http://autopilot.XXX.osdcloud.ch | Out-File -FilePath 'C:\Windows\Setup\scripts\autopilot.ps1' -Encoding ascii -Force

Invoke-RestMethod http://vm.XXX.osdcloud.ch | Out-File -FilePath 'C:\Windows\Setup\scripts\vm.ps1' -Encoding ascii -Force

$UnattendXml = @'
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="specialize">
        <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <RunSynchronous>
                <RunSynchronousCommand wcm:action="add">
                    <Order>1</Order>
                    <Description>Start VM Tasks</Description>
                    <Path>PowerShell -ExecutionPolicy Bypass C:\Windows\Setup\scripts\vm.ps1</Path>
                </RunSynchronousCommand>
                <RunSynchronousCommand wcm:action="add">
                    <Order>2</Order>
                    <Description>Start Autopilot Import & Assignment Process</Description>
                    <Path>PowerShell -ExecutionPolicy Bypass C:\Windows\Setup\scripts\autopilot.ps1</Path>
                </RunSynchronousCommand>
            </RunSynchronous>
        </component>
    </settings>
    <settings pass="oobeSystem">
        <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
            <InputLocale>de-CH</InputLocale>
            <SystemLocale>de-DE</SystemLocale>
            <UILanguage>de-DE</UILanguage>
            <UserLocale>de-CH</UserLocale>
        </component>
    </settings>
</unattend>
'@

Block-WinOS

if (-NOT (Test-Path 'C:\Windows\Panther')) {
    New-Item -Path 'C:\Windows\Panther'-ItemType Directory -Force -ErrorAction Stop | Out-Null
}

$Panther = 'C:\Windows\Panther'
$UnattendPath = "$Panther\Unattend.xml"
$UnattendXml | Out-File -FilePath $UnattendPath -Encoding utf8 -Width 2000 -Force

Step 3: Install during specialize phase

This is the VM.ps1 script that will execute prior to the Autopilot process:

#region VMware Tools installation
If ((Get-CimInstance -ClassName Win32_computersystem).model -like "VMware*") {
    #=================================================
    Write-SectionHeader "Install VMware Tools"
    #=================================================
    $Title = 'Installing VMware Tools'
    $host.ui.RawUI.WindowTitle = "$Title"
    $host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.size(2000, 2000)

    $toolsInstallerPath = "C:\OSDCloud\SW\VMware-tools-latest-x64.exe"
        
        # Check if the download was successful
    if (Test-Path $toolsInstallerPath) {
        Write-DarkGrayHost "Installing VMware Tools from $toolsInstallerPath"
        
        try {
            # Install VMware Tools silently
            $Process = Start-Process -FilePath $toolsInstallerPath -ArgumentList "/S /v`"/qn REBOOT=ReallySuppress ADDLOCAL=ALL REMOVE=CBHelper,FileIntrospection,NetworkIntrospection,ServiceDiscovery,Hgfs,VSS,BootCamp,SaltMinion /l*v C:\OSDCloud\Logs\VMware_Tools_Install.log`"" -NoNewWindow -Wait -PassThru
            
            # Now we can reliably get the exit code
            $exitCode = $process.ExitCode
        
            # Check the exit code
            switch ($exitCode) {
                0 { 
                    Write-DarkGrayHost "VMware Tools installation completed successfully with exit code: 0" 
                }
                3010 { 
                    Write-DarkGrayHost "VMware Tools installation completed successfully with exit code: 3010 (System restart required)" 
                }
                1641 { 
                    Write-DarkGrayHost "VMware Tools installation initiated a restart with exit code: 1641" 
                }
                default { 
                    Write-Host -ForegroundColor Yellow "VMware Tools installation completed with non-standard exit code: $exitCode. Check logs for details." 
                }
            }
        }
        catch {
            Write-Host -ForegroundColor Yellow "Error during VMware Tools installation: $($_.Exception.Message)"
        }
        
        Write-DarkGrayHost "Clean up the installer"
        Remove-Item $toolsInstallerPath -Force
    }
    else {
        Write-DarkGrayHost "No installer file found at $toolsInstallerPath"
    }
}
else {
    Write-DarkGrayHost "Machine is not a virtual machine" -ForegroundColor Yellow
}

Once installed, Windows recognizes the VMXNET3 adapter and Autopilot proceeds with certificate-based authentication (as covered in a previous post).

Outcome

With this approach:

  • No manual driver injection is required during post-deployment.
  • The deployment stays fully automated.
  • We retain the performance benefits of VMXNET3 over E1000.

Key Takeaways

  • Always use the latest VMware Tools package to avoid compatibility issues.
  • OSDCloud simplifies Windows deployments but doesn’t always handle post-WinPE drivers automatically.
  • For VMware VDI environments, dynamic driver installation bridges the gap between WinPE and Autopilot.

Bonus

Helper functions, which I am always using during my OSDCloud deployments.


function Write-DarkGrayDate {
    [CmdletBinding()]
    param (
        [Parameter(Position=0)]
        [System.String]
        $Message
    )
    if ($Message) {
        Write-Host -ForegroundColor DarkGray "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) $Message"
    }
    else {
        Write-Host -ForegroundColor DarkGray "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) " -NoNewline
    }
}
function Write-DarkGrayHost {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, Position=0)]
        [System.String]
        $Message
    )
    Write-Host -ForegroundColor DarkGray $Message
}
function Write-DarkGrayLine {
    [CmdletBinding()]
    param ()
    Write-Host -ForegroundColor DarkGray "========================================================================="
}
function Write-SectionHeader {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, Position=0)]
        [System.String]
        $Message
    )
    Write-DarkGrayLine
    Write-DarkGrayDate
    Write-Host -ForegroundColor Cyan $Message
}
function Write-SectionSuccess {
    [CmdletBinding()]
    param (
        [Parameter(Position=0)]
        [System.String]
        $Message = 'Success!'
    )
    Write-DarkGrayDate
    Write-Host -ForegroundColor Green $Message
}

Leave a Reply

Your email address will not be published. Required fields are marked *