The true install directory of provisioning packages
I’ve been working with provisioning packages for the better part of the last year. They’re a valuable tool for a modern deployment – particularly if you’re blocked from using Autopilot due to limited internet connectivity. But provisioning packages (or, .ppkg’s) are essentially limited to predefined CSP’s or custom scripting, so you’ll eventually find yourself writing a lot of custom scripts.
So in this post, I’ll start by clarifying the true install directory of provisioning packages applied during OOBE:
And for provisioning packages applied to an already deployed device, the install directory is:
If you’re trying to get to the bottom of why a script didn’t run on a device you’ve provisioned, you’ll inevitably need to go to that location to review or rerun the content in the .ppkg. I’m selfishly putting the directory at the top of this article because I keep losing track of where I last wrote it down. The official Microsoft documentation, which otherwise has some great explanation on including custom scripts & cabbing files for the .ppkg deployment, says the directory is in System32. That is no longer the case.
Let’s run through a quick scripting demo using Windows Configuration Designer to see how we can use this information. After opening Windows Configuration Designer, select Provisioning Desktop Devices, name the project, and Switch to advanced editor.
On the left hand side, under Provisioning Commands and Primary Context, type “BGInfo”.
Click Add in the bottom right. You’ll notice that BGInfo turns red, letting us know that an error will occur until we fill in the appropriate fields.
Go to the Windows Sysinternals page to download BGInfo. After extracting the ZIP file, you’ll be able to create a BGInfo config file. You can create custom fields by select the Custom… button. I’ll be tattooing the registry with the .ppkg version, so I’ve added that as a field.
Select Save as after defining any custom properties to create the .bgi file which we’ll use for this demo.
Back in WCD, let’s start by uploading BGInfo64.exe, which is needed to run the config file we just created. You’ll notice that you receive an error if CommandLine is left empty. You can write “cmd /c timeout 1” as a dummy command.
Next, let’s create another PrimaryContext command and upload the config file. After uploading, type “cmd /c ..\0\Bginfo64.exe .\PPKG_BGInfo.bgi /timer:0 /silent” into the CommandLine box. BGInfo64.exe is in another location in the .ppkg directory, which is why it’s referenced by ..\0\.
Now we have a small .ppkg that can be used to customize desktop backgrounds. Since we want this to always run on startup, let’s write a small PowerShell script to copy the file into the startup folder.
Open PowerShell ISE and create a new file with the following command:
“Copy-Item ..\0\bginfo64.exe -Destination ‘C:\programdata\Microsoft\Windows\Start Menu\Programs\StartUp'”
Notice that we’re referencing the relative location again (the “0” folder in the ppkg directory). We can also use this opportunity to set the custom registry setting we defined in the BGInfo config file.
Just like before, create another PrimaryContext command. Upload the PowerShell script, then set the CommandLine to “powershell.exe PPKG_PS.ps1”.
There we have it! You can now export this provisioning package and run it against an out-of-box computer or live one to see the results. But more importantly, in the steps above you’ll notice that I ran across a few key places where I needed to specify the working directory.
When I first ran this package I had missed one of these locations, so I did what I always do – run the provisioning package on a machine, head to the .ppkg directory, and troubleshoot the errors from there.
Since provisioning packages generally run silently, it’s a huge time saver to just head to the directory and run a command versus creating a new .ppkg. Especially when you have multi-gigabyte packages. 🙂