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.

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.

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

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…

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.

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

Let’s do the same thing for Ethernet.

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

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:

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:

Interaction will look like this:

That’s all for now!

SCCM OSD with multiple disks

OSD with systems that have multiple disks can be troublesome. Out of the box, SCCM TS steps don’t offer much in regards to this. You can make Format Drive steps for each disk, but there is no immediate way to identify which disk is which!
Even going into BIOS to check which disk is on which channel won’t help. At each boot, Windows may interpret the disk channels differently: https://support.microsoft.com/en-us/help/937251/disk-drive-numbers-may-not-correspond-to-the-sata-channel-numbers-when

BUT, all hope is not lost. SCCM has a TS variable named OSDDiskIndex. The docs (https://technet.microsoft.com/en-us/library/hh273365.aspx) describe it as “Specifies the physical disk number to be partitioned.”. Hm, specify it how? Well, basically you find the number ID of the disk you want to specify for formatting, and you give that number to the OSDDiskIndex variable. How do we do this? You guessed it, with Powershell of course!

Since Server 2012/Windows 8, there exists the cmdlet Get-PhysicalDisk. You’d think it’d be a part of Powershell 3.0 for Windows 7/2008 R2, but for some reason the storage modules were not included in that (https://blogs.technet.microsoft.com/heyscriptingguy/2012/10/26/use-powershell-to-create-a-bootable-usb-drive/). I don’t know if it’s included in PS 4.0 and higher. But don’t fret, I will show how to accomplish the same thing with a WMI query.

Get-PhysicalDisk

Get-PhysicalDisk is a nice cmdlet. It gives all the relevant information you could wish for from a disk. For OSD, you probably want to differentiate disks on two criteria, SSD/HDD and size.
Getting disk type is easy. The MediaType property of a disk object from the cmdlet describes if the disk is an SSD or HDD. Size is decribed in the… size property! What else do we want? Oh, right the most important one, the ID! This is in DeviceID property.
Output example:

So, in a two disk scenario, where one disk is SSD and you want to select it, the command is as simple as

This will give us the ID of the disk that is an SSD.
Defining on size basis is easy as well (300GB):

For combination of size and SSD:

WMI

This is all fine and dandy if you’re on at least Win8/2012/PE4.0 (or possibly Powershell 4.0 or higher on Win7/2008), but what if you’re not? Good old WMI it is then! Here’s the last example in WMI:

Basically, we use Win32_DiskDrive WMI class to get disk info. Sadly, there is no definite property in WMI that will tell if disk is SSD or not… But in my experience, all SSDs have “SSD” in the model name, so we’ll have to make do with that. Lastly, the ID of the disk is in the Index property.

So now we should know how to find the disk we want to partition. Next, let’s pass this information to the SCCM TS variable OSDDiskIndex, so the TS partition step will select the disk we want!

I find using Run Command Line TS step, with powershell.exe with the Command argument is the easiest way to run one-liners.

In the one-liner, we simply give the OSDDiskIndex variable the ID of the disk.

Of course, if you want to use WMI, replace the second portion of the command argument with the WMI equivalent.

When you run this, that disk will now be selected for later partitioning step.