public-winpe/Invoke-OSDCloudWifi.ps1

<#
.SYNOPSIS
Establishes Wi-Fi connectivity in WinPE environment during OS deployment.
 
.DESCRIPTION
Manages Wi-Fi connection in Windows PE by testing for adapter availability, configuring wireless services, and connecting to available networks. Supports both automated connection via saved Wi-Fi profiles and interactive Wi-Fi network selection. Includes comprehensive checks for required WinPE components, wireless adapter detection, and connection validation with retry logic.
 
.PARAMETER WifiProfile
Specifies the path to a Wi-Fi profile XML file for unattended Wi-Fi connection. If provided and valid, the function attempts to connect using this profile without user interaction. If not provided or invalid, the function presents an interactive Wi-Fi network selection menu.
 
.PARAMETER WirelessConnect
Switch parameter to use the built-in WirelessConnect.exe utility for interactive Wi-Fi connection when available in WinPE. If not specified or unavailable, uses the Get-OSDCloudWifi menu for network selection.
 
.EXAMPLE
Invoke-OSDCloudWifi
Starts the Wi-Fi connection process interactively, displaying available Wi-Fi networks for selection.
 
.EXAMPLE
Invoke-OSDCloudWifi -WifiProfile 'C:\Temp\WifiProfile.xml'
Attempts to connect to Wi-Fi using the specified profile XML file without user interaction.
 
.EXAMPLE
Invoke-OSDCloudWifi -WirelessConnect
Uses the WirelessConnect.exe utility for interactive Wi-Fi connection if it exists in the WinPE environment.
 
.OUTPUTS
None. This function performs Wi-Fi connection setup and writes status messages to the host but does not return objects.
 
.NOTES
This function is designed specifically for Windows PE environments during OS deployment. It performs the following operations:
 
- Tests internet connectivity via Google.com
- Validates WinPE required components (DLL files for wireless support)
- Starts WlanSvc (Wireless LAN Service)
- Detects wireless network adapters
- Attempts to retrieve Wi-Fi profile from HP UEFI firmware if available
- Connects to networks either via stored profile or interactive selection
- Waits for IP configuration and network availability
- Creates transcript logs in $env:Temp\transcript-OSDCloudWifi.txt
 
Required WinPE Components:
  - dmcmnutils.dll
  - mdmpostprocessevaluator.dll
  - mdmregistration.dll
  - raschap.dll
  - raschapext.dll
  - rastls.dll
  - rastlsext.dll
 
If wireless adapters are not detected or drivers are missing, the function will report which devices have errors and may require driver additions to WinPE.
#>

function Invoke-OSDCloudWifi {
    [CmdletBinding()]
    param (
        [System.String]
        $WifiProfile,
        
        [System.Management.Automation.SwitchParameter]
        $WirelessConnect
    )
    #=================================================
    $Error.Clear()
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start"
    #=================================================
    # Start-Transcript
    $LogsPath = "$env:TEMP\osdcloud-logs"

    $Params = @{
        Path        = $LogsPath
        ItemType    = 'Directory'
        Force       = $true
        ErrorAction = 'SilentlyContinue'
    }

    if (-not (Test-Path $Params.Path)) {
        New-Item @Params | Out-Null
    }

    $TranscriptFullName = Join-Path $LogsPath "OSDCloudWifi-$((Get-Date).ToString('yyyy-MM-dd-HHmmss')).log"
    $null = Start-Transcript -Path $TranscriptFullName -ErrorAction SilentlyContinue
    #=================================================
    # Test-OSDCloudInternetConnection
    if (Test-OSDCloudInternetConnection -Uri 'google.com') {
        # Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Ping google.com success. Device is already connected to the Internet"
        $StartOSDCloudWifi = $false
    }
    else {
        # Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Ping google.com failed. Will attempt to connect to a Wireless Network"
        $StartOSDCloudWifi = $true
    }
    #=================================================
    # Test WinPE Required Components
    if ($StartOSDCloudWifi) {
        $RequiredDlls = @(
            'dmcmnutils.dll',
            'mdmpostprocessevaluator.dll',
            'mdmregistration.dll',
            'raschap.dll',
            'raschapext.dll',
            'rastls.dll',
            'rastlsext.dll'
        )
        
        $MissingDlls = @()
        foreach ($Dll in $RequiredDlls) {
            $DllPath = "$ENV:SystemRoot\System32\$Dll"
            if (!(Test-Path -Path $DllPath)) {
                $MissingDlls += $Dll
                $StartOSDCloudWifi = $false
            }
        }
        
        if (!$StartOSDCloudWifi) {
            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to enable Wireless Network due to missing components"
            if ($MissingDlls.Count -gt 0) {
                Write-Host -ForegroundColor Yellow "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Missing DLL files: $($MissingDlls -join ', ')"
            }
        }
    }
    #=================================================
    # Start-Service WlanSvc
    if ($StartOSDCloudWifi) {
        try {
            $WlanService = Get-Service -Name 'WlanSvc' -ErrorAction Stop

            if ($WlanService.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Running) {
                Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] WlanSvc service is already running"
            }
            else {
                Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Starting WlanSvc service from state '$($WlanService.Status)'"
                Start-Service -Name 'WlanSvc' -ErrorAction Stop

                Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Waiting for WlanSvc service to start"
                $WlanService.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Running, [TimeSpan]::FromSeconds(30))
                $WlanService.Refresh()

                if ($WlanService.Status -ne [System.ServiceProcess.ServiceControllerStatus]::Running) {
                    throw "WlanSvc did not reach the Running state within the timeout period."
                }

                Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] WlanSvc service started successfully"
            }
        }
        catch [System.TimeoutException] {
            Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Timed out waiting for WlanSvc service to start"
            $StartOSDCloudWifi = $false
        }
        catch {
            Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to start WlanSvc service: $($_.Exception.Message)"
            $StartOSDCloudWifi = $false
        }
    }
    #=================================================
    # Test Wi-Fi Adapter
    if ($StartOSDCloudWifi) {
        # Do we have a Wireless Interface? We have to search for different names as this will vary depending on the WinPE Language
        $SmbClientNetworkInterface = Get-SmbClientNetworkInterface | Where-Object { ($_.'FriendlyName' -match 'WiFi|Wi-Fi|Wireless|WLAN') } | Sort-Object -Property InterfaceIndex | Select-Object -First 1
        
        # Pair a Wireless Network Adapter based on the InterfaceIndex
        $WirelessNetworkAdapter = Get-CimInstance -ClassName Win32_NetworkAdapter | Where-Object { $_.InterfaceIndex -eq $SmbClientNetworkInterface.InterfaceIndex }

        if ($WirelessNetworkAdapter) {
            $StartOSDCloudWifi = $true
            $WirelessNetworkAdapter | Select-Object * -ExcludeProperty Availability, Status, StatusInfo, Caption, Description, InstallDate, *Error*, *Power*, CIM*, System*, PS*, AutoSense, MaxSpeed, Index, TimeOfLastReset, MaxNumberControlled, Installed, NetworkAddresses,ConfigManager* | Format-List
        }
        else {
            # Get Network Devices with Error Status
            $PnPEntity = Get-WmiObject -ClassName Win32_PnPEntity | Where-Object { $_.Status -eq 'Error' } |  Where-Object { $_.Name -match 'Net' }
            Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] No Wireless Network Adapters were detected"
            if ($PnPEntity) {
                Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Drivers may need to be added to WinPE for the following hardware"
                foreach ($Item in $PnPEntity) {
                    Write-Warning "$($Item.Name): $($Item.DeviceID)"
                }
                Start-Sleep -Seconds 10
            }
            else {
                Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Drivers may need to be added to WinPE before Wireless Networking is available"
            }
            $StartOSDCloudWifi = $false
        }
    }
    #=================================================
    # Test UEFI Wi-Fi Profile
    if ($StartOSDCloudWifi){
        $Module = Import-Module UEFIv2 -PassThru -ErrorAction SilentlyContinue
        if ($Module) {
            $UEFIWifiProfile = Get-UEFIVariable -Namespace "{43B9C282-A6F5-4C36-B8DE-C8738F979C65}" -VariableName PrebootWifiProfile
            if ($UEFIWifiProfile) {
                Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found Wi-Fi Profile in HP UEFI"
                $UEFIWifiProfile = $UEFIWifiProfile -Replace "`0",""

                $SSIDString = $UEFIWifiProfile.Split(",") | Where-Object {$_ -match "SSID"}
                $SSID = ($SSIDString.Split(":") | Where-Object {$_ -notmatch "SSID"}).Replace("`"","")

                $KeyString = $UEFIWifiProfile.Split(",") | Where-Object {$_ -match "Password"}
                $Key = ($KeyString.Split(":") | Where-Object {$_ -notmatch "Password"}).Replace("`"","")
                if ($SSID) {
                    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found $SSID in UEFI, Attepting to Create Profile and Connect"
                    Set-OSDCloudWifi -WLanName $SSID -Passwd $Key -outfile "$env:TEMP\UEFIWifiProfile.XML"
                    if (!($WifiProfile)) {
                        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Setting WifiProfile var to $env:TEMP\UEFIWifiProfile.XML"
                        $WifiProfile = "$env:TEMP\UEFIWifiProfile.XML"
                    }
                }
            }
        }
    }
    #=================================================
    # Test Wi-Fi Connection
    #TODO Test on ARM64
    if ($StartOSDCloudWifi) {
        if ($WirelessNetworkAdapter.NetEnabled -eq $true) {
            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Wireless is already connected ... Disconnecting"
            (Get-WmiObject -ClassName Win32_NetworkAdapter | Where-Object { $_.InterfaceIndex -eq $WirelessNetworkAdapter.InterfaceIndex }).disable() | Out-Null
            Start-Sleep -Seconds 5
            (Get-WmiObject -ClassName Win32_NetworkAdapter | Where-Object { $_.InterfaceIndex -eq $WirelessNetworkAdapter.InterfaceIndex }).enable() | Out-Null
            Start-Sleep -Seconds 5
            $StartOSDCloudWifi = $true
        }
    }
    #=================================================
    # Connect
    if ($StartOSDCloudWifi) {
            if ($WifiProfile -and (Test-Path $WifiProfile)) {
                Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Starting unattended Wi-Fi connection "
            }
            else {
                Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Starting Wi-Fi Network Menu "
            }

            # Use the Win32_NetworkAdapterConfiguration to check if the Wi-Fi adapter is IPEnabled
            while (((Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration | Where-Object { $_.Index -eq $($WirelessNetworkAdapter.DeviceID) }).IPEnabled -eq $false)) {
            Start-Sleep -Seconds 3

            $StartOSDCloudWifi = 0
            # make checks on start of evert cycle because in case of failure, profile will be deleted
            if ($WifiProfile -and (Test-Path $WifiProfile)) { ++$StartOSDCloudWifi }
    
            if ($StartOSDCloudWifi) {
                # use saved wi-fi profile to make the unattended connection
                try {
                    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Establishing a connection using $WifiProfile"
                    Connect-OSDCloudWifiByXMLProfile $WifiProfile -ErrorAction Stop
                    Start-Sleep -Seconds 10
                }
                catch {
                    Write-Warning $_
                    # to avoid infinite loop of tries
                    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Removing invalid Wi-Fi profile '$WifiProfile'"
                    Remove-Item $WifiProfile -Force
                    continue
                }
            }
            else {
                # show list of available SSID to make interactive connection
                if (($WirelessConnect) -and (Test-Path -path $ENV:SystemRoot\WirelessConnect.exe)) {
                    Start-Process -FilePath  $ENV:SystemRoot\WirelessConnect.exe -Wait
                }
                else {
                    $SSIDList = Get-OSDCloudWifi
                    if ($SSIDList) {
                        #show list of available SSID
                        $SSIDList | Sort-Object Index | Select-Object Signal, Index, SSID, Authentication, Encryption, NetworkType | Format-Table
            
                        $SSIDListIndex = $SSIDList.index
                        $SSIDIndex = ""
                        while ($SSIDIndex -notin $SSIDListIndex) {
                            $SSIDIndex = Read-Host "Select the Index of Wi-Fi Network to connect or CTRL+C to quit"
                        }
            
                        $SSID = $SSIDList | Where-Object { $_.index -eq $SSIDIndex } | Select-Object -exp SSID
            
                        # connect to selected Wi-Fi
                        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Establishing a connection to SSID $SSID"
                        try {
                            Connect-OSDCloudWifi $SSID -ErrorAction Stop
                        } catch {
                            Write-Warning $_
                            continue
                        }
                    } else {
                        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] No Wi-Fi network found. Move closer to AP or use ethernet cable instead."
                    }
                }
            }

            if ($StartOSDCloudWifi) {
                $text = "to Wi-Fi using $WifiProfile"
            } else {
                $text = "to SSID $SSID"
            }
            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Waiting for a connection $text"
            Start-Sleep -Seconds 15
        
            $i = 30
            #TODO Resolve issue with WirelessNetworkAdapter
            while (((Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration | Where-Object { $_.Index -eq $($WirelessNetworkAdapter.DeviceID) }).IPEnabled -eq $false) -and $i -gt 0) {
                --$i
                Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Waiting for Wi-Fi Connection ($i)"
                Start-Sleep -Seconds 1
            }
        }
        Get-SmbClientNetworkInterface | Where-Object { ($_.FriendlyName -match 'WiFi|Wi-Fi|Wireless|WLAN') } | Format-List
    }
    $null = Stop-Transcript -ErrorAction Ignore
    if ($StartOSDCloudWifi) {
        Start-Sleep -Seconds 5
    }
}