PSADT interaction in an SCCM task sequence

PSADT is a great toolkit. I use it for all our SCCM applications and packages. In fact, the toolkit is installed as a module on all systems through GPO. The cmdlets it provides are excellent (Get-InstalledApplication and
Invoke-HKCURegistrySettingsForAllUsers being personal favorites). PSADT is so great, there’ll be many posts in the future containing it.

Anyway, this post is about PSADT in a task sequence. This need was born out of the need to perform an organization wide Windows 7 to 10 upgrade using a SCCM TS. TS’s are out-of-the-box without any sort of interactivity, but with PSADT and ServiceUI.exe magic we can make it happen.
So, using the upgrade scenario as an example, what we want is:

1. Have the upgrade available to users in software center, so they can install it at their own leisure
2. After a certain date, make the deployment required.

Required task sequences are tricky. You can make people easily angry with that kind of stuff. So, a deferral option has to be in there. What else? Well, pre-flight checks in general would be. We want to check that user has power connected, as well as wired network connection, for starters.

PSADT has a deferral system. In a script, specifically in the Show-InstallationWelcome cmdlet, you can specify how many times a user is allowed to defer. After the user has deferred the allotted times, the prompt will be inescapable. Lovely!

So, on to the PSADT script. You may have the code in either the pre-installation or installation section of Deploy-Application.ps1. Doesn’t really matter since we’re just running PoSH code, not doing any installation.

Let’s start with pre-flight checks. Let’s assume first that AC is plugged, if the system has a battery, we’ll actually check it, by using WMI.

$ACplugged = $true
If (Get-WmiObject Win32_Battery) { 
			
    $ACplugged = Get-WmiObject -Class BatteryStatus -NameSpace Root\Wmi | Select-Object -ExpandProperty PowerOnline
}

Network connections. Let’s check for a wired connection as well as wireless. In our case, we don’t want to allow upgrades over VPN. Depending on VPN, you may need to modify so the variable suits your environment. Included WMI query checks for Palo Alto GlobalProtect.

$wiredNIC = Get-WmiObject -Class Win32_NetworkAdapter -NameSpace root\CIMV2 | Where-Object {$_.AdapterType -match "Ethernet" -and $_.NetConnectionStatus -eq 2 -and $_.Name -notmatch "Wireless" -and $_.Name -notmatch "PANGP"}

$WiFiNIC = Get-WmiObject -Class Win32_NetworkAdapter -NameSpace root\CIMV2 | Where-Object {$_.AdapterType -match "Ethernet" -and $_.NetConnectionStatus -eq 2 -and $_.Name -match "Wireless"}

$VPN = Get-WmiObject -Class Win32_NetworkAdapter -NameSpace root\CIMV2 | Where-Object {$_.Name -match "PANGP" -and $_.NetConnectionStatus -eq 2}

Next, let’s invoke TS environment to the $TSEnv variable.

$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment

Here we set TS error dialog timeout to one second. Doesn’t look cool if the user defers and they still have an error dialog for 20 minutes…

$TSEnv.Value("SMSTSErrorDialogTimeout") = "1"

For clear visibility of our PSADT prompt, let’s close the TS progress UI, but only if a user is logged on. $usersLoggedOn is a PSADT built-in variable.

If ($usersLoggedOn) { (New-Object -COMObject Microsoft.SMS.TSProgressUI).CloseProgressDialog() }

Now, if a user is on VPN, we’ll inform the user to come to the office for the upgrade. Then, we exit with code 60012 (PSADT deferral code).

If ($VPN) {

    $deferInstallation = Show-InstallationPrompt -Message 'Unfortunately, Windows 10 upgrade is not supported over VPN. Please do it on premises.' -ButtonMiddleText 'OK' -Icon 'Exclamation' -PersistPrompt

    Exit-Script -ExitCode 60012
}

If AC power is not available, let’s inform that the user has to plug it in for the upgrade to continue. There’s also an option to defer to run the upgrade at another time.
To accomplish this, I believe it’s best to use a While loop.

While (!$ACplugged) {

    If (!$usersLoggedOn) { Exit-Script -ExitCode 60012 }
			
    ElseIf ($usersLoggedOn) {

        $deferInstallation = Show-InstallationPrompt -Title "Windows 10 upgrade" -Message "AC power is disconnected! Please plug in your PC and select continue, if you want to run the upgrade now. You may also defer installation to another time." -ButtonLeftText "Continue" -ButtonRightText "Defer" -Icon 'Exclamation' -PersistPrompt
        
        If ($deferInstallation -eq "Defer") { Exit-Script -ExitCode 60012 } 
				
        ElseIf ($deferInstallation -eq "Continue") {

            Start-Sleep -Seconds 4
            $ACplugged = Get-WmiObject -Class BatteryStatus -NameSpace Root\Wmi | Select-Object -ExpandProperty PowerOnline
        }
    }        
}

Let’s do the same thing for Ethernet.

While (!$wiredNIC) { 

    $deferInstallation = Show-InstallationPrompt -Title "Windows 10 upgrade" -Message "Wired network is disconnected! Please plug in your PC and select continue, if you want to run the upgrade now. You may also defer installation to another time." -ButtonLeftText "Continue" -ButtonRightText "Defer" -Icon 'Exclamation' -PersistPrompt
           
	If ($deferInstallation -eq "Defer") { Exit-Script -ExitCode 60012 } 
			
	ElseIf ($deferInstallation -eq "Continue") {

        Start-Sleep -Seconds 5
        $wiredNIC = Get-WmiObject -Class Win32_NetworkAdapter -NameSpace root\CIMV2 | Where-Object {$_.AdapterType -match "Ethernet" -and $_.NetConnectionStatus -eq 2 -and $_.Name -notmatch "Wireless" -and $_.Name -notmatch "PANGP"}
    }
}

Lastly, if both Ethernet and Wifi is enabled, let’s disconnect from the Wifi network.

If ($WiFiNIC) {

    netsh wlan disconnect
    Start-Sleep -Seconds 2
}

If all checks out, let’s inform the user of the pending upgrade, and allow deferral. Let’s also prompt for the closing of applications. We can do this with the Show-InstallationWelcome cmdlet.
If you want custom text in this cmdlet, you need to edit AppDeployToolkitConfig.xml inside the respective language tag, as well as set -CustomText to true in the command. Like this:

Show-InstallationWelcome -CloseApps 'iexplore=Internet Explorer,chrome=Chrome,firefox=Firefox,winword=Word,excel=Excel,outlook=Outlook,powerpnt=PowerPoint' -AllowDefer -DeferTimes 10 -PersistPrompt -CustomText $true
}

And that’s pretty much the script. Of course, consider this a template and modify to your needs. Please comment if you have some handy additions!

Now, to include this in a task sequence, you’ll want to include it as the first step. For user interaction you need to provide ServiceUI.exe in the package (Get it from MDT).
Finally, the command line will be:

ServiceUI.exe Deploy-Application.exe

Interaction will look like this:

That’s all for now!

Leave a Reply

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