Adding support for OS deployment through Wi-Fi to SCCM

Adding support for OS deployment through Wi-Fi to SCCM

So you can reimage your devices on any Wi-Fi network

Subscribe to my newsletter and never miss my upcoming articles

[edited] 9.7.2021 - added section 4. Automatically use Wi-Fi profile from the running OS

[edited] 25.6.2021 - added section Adding Wi-Fi support to Boot Image imported into SCCM

[edited] 19.6.2021 - IMPORTANT - solved timeouts problem! Check section 3. Making Wi-Fi connection persistent during OS installation reboots

[edited] 17.6.2021 - added workaround for startup timeouts

In this post, I will show you, how to add Wi-Fi support for OSD to SCCM. This can be really helpful if, for example, you want to let your remote employees reimage their computers using a home Wi-Fi network. So for this particular example, I will use SCCM's Cloud Management Gateway (CMG) component as an installation source i.e. installation data will be downloaded from the Internet instead of an on-premise SCCM server.

Table of Contents


Prerequisites

  • SCCM (MEMCM)
    • CMG (only if you want to make the installation through the Internet)
  • OSDCloud PowerShell module
  • USB flash drive
  • Wi-Fi network (WPA-personal)
  • PowerShell console
  • Admin rights

Three problems to solve

  • We need a boot image, that supports Wi-Fi
  • Wi-Fi connection has to be established before the Task Sequence checks kick in
  • Wi-FI connection has to survive restarts during the installation process

1. Adding Wi-Fi support into the boot image

This problem is quite easy to solve, thanks to the great work of David Segura and his OSDCloud tool.

So the steps are as follows:

  • Create WinPE boot image with Wi-Fi support using OSDCloud
  • Upload this boot image to SCCM
  • Create Bootable Task Sequence Media
  • Deploy Task Sequence Media to USB drive

Create WinPE boot image with Wi-Fi support using OSDCloud

Download and install Windows ADK + WinPE support as stated in OSDCLoud requirements

In Administrator PowerShell console run:

Install-Module OSD -Force
Import-Module OSD -Force

# WinRE instead of WinPE to support Wi-Fi
New-OSDCloud.template -WinRE -Verbose
# create OSDCloud workspace folder
$WorkspacePath = "C:\temp\OSDCloud"
New-OSDCloud.workspace -WorkspacePath $WorkspacePath

# add general Wi-Fi NIC drivers to boot image
Edit-OSDCloud.winpe -CloudDriver Dell, HP, Nutanix, VMware, WiFi
# if you need to add some custom drivers, use
# Edit-OSDCloud.winpe -DriverPath "C:\myCustomDrivers"

After running the command above, there should be boot.wim in the C:\temp\OSDCloud\Media\Sources folder.

Upload OSDCloud boot image to SCCM

Import created boot image (boot.wim) into the Boot Images image.png

Create Bootable Task Sequence Media

image.png

Just don't forget to select imported Boot Image!

image.png

Deploy Task Sequence Media to USB drive

Use Rufus or similar application to create bootable USB drive from created Bootable Task Sequence Media ISO.


2. Initialize Wi-Fi before Task Sequence network checks kick in

Wi-Fi enabled boot image created by OSDCloud lets you connect to the Wi-Fi network by default. The problem is, that it happens after Task Sequence inner checks. To fix this, we have to customize the default behavior, to initialize Wi-Fi as soon as possible after the WinPE starts. Otherwise, the installation will fail with error image.png

Customize OSDCloud boot image to support Wi-Fi in SCCM OS installation

Several customizations have to be done in boot.wim saved on USB drive created from Bootable Task Sequence Media ISO, we have created in the previous section. Therefore I've created PowerShell script customize_sccm_usb_boot_image_to_support_wifi.ps1 for you.

Just download and run the script in administrator PowerShell console to make the necessary modification of the connected USB boot drive.

TIP: we are modifying Bootable Task Sequence Media boot image instead of the earlier generated OSDCloud image because SCCM customizes boot.wim content automatically (winpeshl.ini especially) when you: import boot image, redistribute it or Bootable Task Sequence Media image is created. Therefore our customization would be removed.

It will automatically:

  • Find USB drive that contains boot.wim
  • Mount that boot.wim
  • Customize winpeshl.ini, to initialize Wi-Fi ASAP after the WinPE start
    • If you are interested why not use the Prestart command, it is because it is being run too late in the startup process. On contrary winpeshl.ini si processed by winpeshl.exe, which is run right after winlogon.exe kicks in
  • Customize OSDCloud Set-WinREWiFi function, to omit removal of exported Wi-Fi xml profile (persistence preparation)
  • Customize startnet.cmd, to omit OSDCloud builtin attempt, to initialize Wi-Fi (we already done that)
    • (optional) Create/copy XML Wi-Fi profile to \Windows\Temp\wprofile.xml (for unattended Wi-Fi connection)
  • Save and dismount boot.wim

TIP: There is also an option to integrate Wi-Fi credentials for making unattended connection! Just use parameter wifiCredential or xmlWiFiProfile when you call this script. For more details check help in the script itself.

This is how the unattended Wi-Fi connection will look like, when you boot from created USB. unattended wifi connection.jpg

Adding Wi-Fi support to Boot Image imported into SCCM

To use boot image that supports Wi-Fi not just to start Task Sequence from USB, but also from running OS (i.e. boot.wim will be downloaded from MP), you will have to do one modification in sccm before importing it.

Because SCCM replaces the existing winpeshl.ini on boot image when you import it or do any action with it, you have to hack it a little bit. To be more specific, we will force SCCM to use our custom winpeshl.ini instead of the default one. To manage that, you have to create winpeshl.ini in [SCCM Install directory]\OSD\bin\x64\ (or i386 for 32-bit boot images) with the following content and do it before you import previously created and customized Wi-Fi capable boot.wim into SCCM!

[LaunchApps]
PowerShell.exe, -NoProfile -NoLogo -ExecutionPolicy Bypass -File connectWifi.ps1
%SYSTEMDRIVE%\sms\bin\x64\TsBootShell.exe

This will force SCCM to use this winpeshl.ini instead of the default one, every time, any action with any boot image will take place! So it's a good idea to remove this file when the boot image will be safely stored on Management Point.

Thanks Jan-Kristian Arntzen for pointing this out!


3. Making Wi-Fi connection persistent during OS installation reboots

To automatically connect to Wi-Fi you only need two things:

  • Wi-Fi profile exported to an XML file
  • Logic, that will connect to Wi-Fi specified in such XML file after each restart

The first part is halfway solved thanks to CloudOSD function Start-WinREWiFi, which automatically creates such XML (during interactive connection to Wi-Fi at start of WinPE). And the customization we've made in the previous section (to not delete this profile after making a connection). So we just have to copy this file to a freshly installed OS drive (I've chosen C:\Windows\WCFG\wprofile.xml as a destination).

The second part will be solved by customizing the startup command defined in cmdline registry value. The file defined in this registry will be automatically started at the system startup. SCCM itself is using this registry value to manage Task Sequence installation (by running osdsetuphook.exe /execute). There is also a second important registry value and that is setupType for defining how to treat with cmdline.

As an easy solution, I have created several helper Task Sequences, that I use as sub-TS in my main deployment TS. It makes the process much easier to maintain and reuse, but of course, you can extract the logic and use it as common TS steps.

Helper Task Sequences

All these helper Task Sequences are stored in my GitHub repository, so you can easily import them into your SCCM and use them as described in your own deployment Task Sequence.

COPY Wi-Fi PROFILE TO OS DISK

  • What it does
    • Search for XML Wi-Fi profile in %TEMP% folder and copies it to newly installed OS disk (C:\Windows\WCFG\wprofile.xml), so it is possible to import it later, to maintain Wi-Fi connection persistence after each restart.
      • Such XML profile could have been created by OSDCloud Start-WinREWiFi function or by running my customize_sccm_usb_boot_image_to_support_wifi.ps1 script
    • Creates TS variable WifiProfile so it is possible to detect later, that the installation is running through a Wi-Fi connection
  • When to use it
    • Has to be run in WinPE right after OS installation step
  • Called script content
Start-Transcript "$env:TEMP\wProfileCopy.log"
# Get-Volume seems to not work in winpe
$OSDrive = Get-WmiObject win32_logicaldisk | ? { $_.VolumeName -eq "Windows" } | select -exp DeviceId
if (!$OSDrive) { throw "Unable to find OS drive. Did you run this step RIGHT AFTER OS was installed?" }

# location where Wi-FI profile will be stored
$WCFG = "$OSDrive\Windows\WCFG"
# create if doesn't exists
[Void][System.IO.Directory]::CreateDirectory($WCFG)

# search for saved Wi-Fi xml profile in TEMP
# OSDCloud function Start-WinREWiFi saves it there as <SSID>.xml
Get-ChildItem $env:TEMP -File -Filter *.xml | % {
    $fileBaseName = $_.BaseName
    $filePath = $_.FullName

    # if Wi-Fi profile is found, copy it to newly installed OS DISK
    if (([xml](Get-Content $filePath)).WLANProfile.Name) {
        # RECONNECT TASK SEQUENCE STEP EXPECTS such name and location
        $wifiProfile = Join-Path $WCFG "wprofile.xml"
        "copying Wi-Fi profile $filePath to $wifiProfile"
        Copy-Item $filePath $wifiProfile -Force -ea Stop
        # this string will be saved to WifiProfile TS Variable and will be used to detect, that installation is running using Wi-Fi
        return $wifiProfile
    }
}

Write-Warning "No Wi-Fi profile was found in $env:TEMP"

CUSTOMIZE STARTUP COMMAND

  • What it does
    • Customizes cmdline and setuptype registry values plus creates helper batch script, so the first action after OS restart will be establishing connection to Wi-Fi
  • When to use it
    • Has to be run right after step Setup Windows and Configuration Manager i.e. first start of the installed OS
  • Called script content
# customize startup command to prefix whatever there is, with connecting to Wi-Fi

if ($env:TEMP -match "^X:") {
    Write-Error "Don't run this in WinPE! Unless you've figured out, how to start Wi-Fi after first OS start"
    Start-Sleep 10
    exit
}

# Get-Volume seems to not work in winpe
$OSDrive = Get-WmiObject win32_logicaldisk | ? { $_.VolumeName -eq "Windows" } | select -exp DeviceId

# location where Wi-FI profile should be stored
$WCFG = "$OSDrive\Windows\WCFG"
# location of the script, that will be called in cmdline registry value
$helperScript = "$WCFG\custom_cmdline.cmd"
# location of the Wi-Fi profile 
$wifiProfile = "$WCFG\wprofile.xml"
# SSID of the Wi-Fi to connect
$SSID = ([xml](Get-Content $wifiProfile)).WLANProfile.Name
# startup command
$cmdLine = Get-ItemPropertyValue "HKLM:\SYSTEM\Setup" -Name cmdline
# from some reason the cmdline in SCCM TS looks like: system32\osdsetuphook.exe /execute
# i.e. it is not absolute path, which causes trouble (boot loop, because of non existent path), when you call it from script
if ($cmdLine -and $cmdLine -notmatch "^$OSDrive\\Windows\\") {
    $cmdLine = "$OSDrive\Windows\$cmdLine"
}


if (!$OSDrive) { throw "Unable to find OS drive. Did you run this step AFTER OS was installed?" }

if (!(Test-Path $wifiProfile)) { throw "This shouldn't happen, because this step should be run ONLY if WifiProfile TS variable is filled" }

if (!$SSID) { throw "Unable to extract SSID from $wifiProfile" }


# ensure, that cmdline registry value contains call to my helper script
if ($cmdLine -eq $helperScript) {
    # it does >> no change is needed
    "No change needed"
    return
} else {
    # it doesn't >> I have to create new helper script with registry key command + add my own command to it + call this script in cmdline
    "CmdLine has to be customized"

    $myCommand = @"
@echo off
echo MAKING Wi-FI CONNECTION

REM start Wi-Fi service if needed
SC query wlansvc | FIND /i "RUNNING" > nul
IF ERRORLEVEL 1 (
 echo Starting wlansvc
 SC start wlansvc
 ping 127.0.0.1 -n 10 > nul
) else (
 echo wlansvc was already running
 ping 127.0.0.1 -n 3 > nul
)

REM add Wi-Fi profile
netsh wlan add profile filename="$wifiProfile"
IF ERRORLEVEL 1 (
 REM Wi-Fi adapter is not initialized yet
 echo Giving this a second chance
 ping 127.0.0.1 -n 15 > nul
 netsh wlan add profile filename="$wifiProfile"
 IF ERRORLEVEL 1 (
  echo Unable to add Wi-Fi profile. Wi-Fi adapter is probably missing
  echo ######## Existing NIC^(s^)
  wmic nic get name, index, NetEnabled
  echo ######## Existing Interface^(s^)
  netsh interface show interface
  ping 127.0.0.1 -n 15 > nul
  exit 1
 )
)

REM connect to Wi-Fi
echo Connecting to $SSID
netsh wlan connect name="$SSID" ssid="$SSID"

REM wait for connection
echo Waiting for internet connection
:loop
(ping -n 1 www.google.com | find "TTL=") > nul || goto :loop
echo Success!

REM run command, that was originally mentioned in cmdline value
echo Running original cmdline
$cmdLine
"@

    "NEW CONTENT OF cmdline WILL BE"
    $myCommand

    $myCommand | Out-File $helperScript -Force -Encoding ascii
    # replace original value with my script
    Set-ItemProperty "HKLM:\SYSTEM\Setup" -Name cmdline -Value $helperScript
    # SetupType https://social.technet.microsoft.com/Forums/windows/en-US/b942a34d-c4a7-489c-bb01-45dd65fa9b20/setuptype-and-cmdline-at-hkeylocalmachinesystemsetup?forum=itproxpsp
    Set-ItemProperty "HKLM:\SYSTEM\Setup" -Name setuptype -Value 2 -Type Dword
}

DISABLE RE-TRIES FOR SENDING STATUS MSG BACK TO MP

  • What it does
    • As the name suggests, this step disables re-tries for sending Status Messages back to SCCM MP
  • When to use it
    • To avoid timeouts in case, there is no network connection
    • That is the reason I use it before Setup Windows and Configuration Manager step, because this is the only place when Wi-Fi adapter is not yet ready (check TODO section)

RESTART

  • There are two Task Sequences for restart

    • RESTART (to OS)
    • RESTART (to WinPE)
  • What it does

    • Restart
    • Ensures, that connecting to Wi-Fi will be made as the first step after the restart
  • When to use it
    • Use it if you need to make a restart (+ connect to Wi-Fi after)

REMOVE Wi-Fi CONNECTION DATA

  • What it does
    • Removes whole folder C:\Windows\WCFG i.e. deletes Wi-Fi XML profile file
    • Removes added Wi-Fi profile from installed OS
  • When to use it
    • Has to be run as a final step of your TS
  • Called script content
# location where Wi-Fi connection data should be stored
$WCFG = "$env:SystemRoot\WCFG"
$wifiProfile = "$WCFG\wprofile.xml"

# disconnecting from the Wi-Fi
if (Test-Path $wifiProfile) {
    # get SSID
    $SSID = ([xml](Get-Content $wifiProfile)).WLANProfile.Name

    "Removing Wi-Fi profile $SSID"
    $null = netsh wlan delete profile "$SSID"
}

# remove configuration data (Wi-Fi password is there in plaintext!)
if (Test-Path $WCFG) {
    "Removing '$WCFG'"
    Remove-Item $WCFG -Recurse -Force
} else {
    "'$WCFG' not Found"
}

How resultant Task Sequence should look like

image.png You can download it from my GitHub Repository and import it into your SCCM console.

  1. The Wi-FI profile is copied from WinPE environment to installed OS
  2. Disable retries for sending Status Messages back to MP. To eliminate timeouts in Setup Windows and Configuration Manager step, where Wi-Fi is not working yet
  3. Customize OS startup to initiate Wi-Fi connection as the first thing after the restart, restart the computer (because right now you are offline :( ) and enable sending of Status Messages, so you can monitor progress on your MP
  4. Again disable retries for sending Status Messages back to MP. To eliminate timeouts after removal of the Wi-Fi profile connection (it will immediately disconnect the Wi-Fi)

4. Automatically use Wi-Fi profile from the running OS

To further make the installation through Wi-Fi connection easier, we can set Task Sequence to reuse the existing Wi-Fi connection from the OS that is being reinstalled. As a result, user won't be prompted to make a Wi-Fi connection and the same Wi-Fi network he was connected to will be used for OSD too automatically.

This can be of course used only in cases when the installation is invoked from running OS (via Software Center).

To make this work, we need two new Task Sequence steps:

So to make this work, download these two Task Sequences, import them to your SCCM Task Sequence list and use them in your own Task Sequence like image.png Also make sure to use Wi-Fi supporting boot image in your SCCM i.e. follow Adding Wi-Fi support to Boot Image imported into SCCM. Because this boot image will be downloaded from SCCM in case of invoking OS installation from the running OS.

!!! There was a bug in customize_sccm_usb_boot_image_to_support_wifi.ps1 script, that I fixed 9.7.2021. So to make this work, use this new fixed version to customize your Wi-Fi supporting boot image AND redistribute it to your SCCM DP's!!!


Summary

If you got to this point, you are ready to reimage any of your devices on any personal Wi-Fi network. To accomplish this task, we've created Wi-Fi supporting boot image using OSDCloud tool, customized it a little bit to match SCCM needs, and customized our deployment Task Sequence to make Wi-Fi connection persistent.

Moreover, we can import Wi-Fi profile directly to boot image or set the Task Sequence to reuse the existing Wi-Fi connection from OS that is being reinstalled.

Have fun 👍


Useful information


OS installation screenshots

Connecting to Wi-Fi

  • Manually connect to wifi.webp

  • Unattended connect to wifi unattended.webp

Choosing TS choose TS.webp

Installing OS image.png

Reconnecting to Wi-Fi after the restart 20210619_182311.jpg


TODO

Solve connecting the Wi-Fi in step Setup Windows and Configuration Manager. Right now this is the only step, where Wi-Fi is not working, so I have to do workarounds like disabling re-tries for sending Status Messages to MP. Therefore in your Task Sequence restart ASAP after this step to make Wi-Fi working i.e. do it as I did in my example of Task Sequence :)

PS: I already tried to initialize network adapters by netcfg -v -winpe plus start the wlansvc service, but it ended in start-pending state...

Interested in reading more such articles from Ondrej Sebela?

Support the author by donating an amount of your choice.

 
Share this