Recently I discovered the Download Package Content task. You would use this mainly to quickly download some files into your task sequence environment. This could be in the normal OS, or during OSD in WinPE. Recently I had a use case why this task comes in handy.

Options to configure

In this step you can configure to download multiple packages from different sources. You can download Boot Images, Operating System Images, Operating System Upgrade Packages, Driver Packages and Packages in any combination you would like.

The benefit of this step, is that you can assign a custom task sequence variable for each package that you download. This variable will spit out the download path of the contents that is downloaded. When you assign a variable, you have to place a numeral suffix after the variable in the order of the packages you’ve added. This means for example, if WorkingDirectory is used as variable, you would need to call WorkingDirectory01 for the first package that has been downloaded. Even when there is only one package!

Looking back at my other post, I discovered that I repeated several tasks in my in-place upgrade task sequence. For every model we support, there is a normal package available. The task will download the package content in a Run a Command Line task. Works like a charm, but we have 14 models. Repeating every step 14 times is not ideal. It’s simply not scalable.

Using 14 Run Command Line steps is not scalable

Configuration example

In the screenshot below you will find an example of one of the Download Package Content tasks in the upgrade task sequence. This task wil only run if the model running the task sequence is a Dell OptiPlex 3020.

Create multiple download package content tasks and store the path as a variable

I used the option to store a custom task sequence variable to store the download path of the package and used it in a PowerShell script. The scripts reads the variable and sets the working directory to the download location, with the result that you only have one PowerShell script to maintain.

To read a task sequence variable in PowerShell, use the code below. Replace DriverVariable01 to your own custom variable.

#Read SCCM TS Variable Environment
$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$path = $tsenv.Value("DriverVariable01")

Full PowerShell script

Below the full PowerShell script containing:

  • Basic logging to a .txt-file on the C:\ drive.
  • Reading the task sequence variable and set the working directory to that path.
  • Grabbing the .inf-files of all drivers and install them using pnputil.exe.
  • Check the exit code of pnputil and act accordingly.
#Variables
$file = "C:\$env:COMPUTERNAME.txt"

#TimeStamp function
function Get-TimeStamp {
    return "[{0:dd/MM/yy} {0:HH:mm:ss}] - Drivers -" -f (Get-Date)
}

#Defines SCCM TS Variable Environment
$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$path = $tsenv.Value("DriverVariable01")

Write-Output "$(Get-TimeStamp) Reading driver path from variable. Path is: $path" | Out-File $file -Append

#Set location of package content downloaded by Task Sequence
Write-Output "$(Get-TimeStamp) Set working directory to the folder containing drivers" | Out-File $file -Append
Set-Location "$path"

#Log the current working directory
$currentlocation = Get-Location
Write-Output "$(Get-TimeStamp) Working directory is now: $($currentlocation.Path)" | Out-File $file -Append

#Let log file know that we're going to start with updating drivers
Write-Output "$(Get-TimeStamp) Going to make a start with updating drivers" | Out-File $file -Append

#Executing PnPUtil to place .inf files on the target machine
$pnputil = Start-Process -FilePath pnputil.exe -ArgumentList "/add-driver "".\*.inf"" /subdirs /install" -PassThru -NoNewWindow -Wait
$pnputil

#Check exitcode to see if things are ok
if (!($pnputil.ExitCode -eq "0" -or "3010 " -or "1641" -or "259")) {
    Write-Output "$(Get-TimeStamp) Process failed with exit code: $($pnputil.ExitCode). Please review https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes" | Out-File $file -Append
    Return 1
}

else {
    Write-Output "$(Get-TimeStamp) Process succeeded with exit code: $($pnputil.ExitCode). Drivers are installed" | Out-File $file -Append
    Return 0
}

Wrapping things up

With this approach you are able to minimise the effort of maintaining tasks separately. Creating one PowerShell script, you’re also very flexible in logging the execution of several tasks you need to get things installed, or configured. This script could be a stepping stone to start self-remediation in a task sequence, when something failed.