A few days ago, I started working on a new script. This blog is not about that script, but rather the unrelated issues I encountered while working on that script. Let’s start with a little history lesson of the two existing scripts.
Introducing Autopilot Branding
Just over a year ago, I published two blogs (first and second) about a package of customizations that you could customize and package into an MSI that you could deploy to your devices via Intune as part of a Windows Autopilot deployment. The types of things that can be configured include:
- Apply a default Start menu layout
- Apply a default desktop wallpaper
- Set the time zone
- Remove a list of in-box provisioned apps
- Install the latest OneDrive client (per machine)
- Disable the (old) Edge desktop icon
- Install language packs
- Configure language settings
- Install features on demand
All of this can be found in GitHub at https://github.com/mtniehaus/AutopilotBranding. You can download it (or clone the repository), customize it as required, and build it (after installing Wix). It was a non-trivial process (necessitated at the time by the lack of support for tracking Win32 apps during ESP, something rectified in Windows 10 1903 and above), but quite a few people have used it. Before you try it, though, read on – I’ve changed the process considerably to simplify the configuration, setting up the included PowerShell script as a Win32 app directly, without needing an MSI.
Introducing Update OS
About six months ago, I published another blog talking about another new script called UpdateOS. This one performs a simple task: It leverages the PSWindowsUpdate PowerShell module to identify any needed Windows updates that need to be installed, then downloads them and installs them. So by the time Autopilot is done with the device setup, the device is fully patched.
Since this one was released after Windows 10 1903, I used a different mechanism to deploy this one: Set it up as a Win32 app that runs the PowerShell script directly. From an Intune perspective, it behaves like any other app, but really it’s just performing a one-time update process.
When 1 + 1 = 0
As I mentioned previously, I was working on a new script, adding it into the other configurations, including the two apps above, that I routinely deploy to each device. But before it even got to my new script, I could see the UpdateOS script was failing. As that script logs what it is doing to C:\ProgramData\Microsoft\UpdateOS, I could see that it was trying to install the May cumulative update, KB4556799, on top of my Windows 10 1909 installation. But that cumulative update was routinely failing to install. The Windows Update log showed a generic error (80070002), which wasn’t helpful. Looking deeper into the CBS.LOG, I could see some interesting activity: The .NET Framework was being installed around the same time as the cumulative update, which can’t be a good thing.
OK, so I need to do some sequencing: Ideally the Autopilot Branding app would run first, and then the Update OS app would run. That way, the additional features (e.g. the .NET Framework) being added by the Autopilot Branding app would get patched and there would be no need to reinstall the latest cumulative update. The first step to setting up that dependency was to move the Autopilot Branding app from a Windows MSI line-of-business app to a Win32 app. That’s not too difficult, packaging the .MSI file into a .INTUNEWIN file, uploading it to Intune, and configuring the appropriate command lines and detection rules. At that point, I could set up a dependency that says Autopilot Branding is a dependency of Update OS, so Autopilot Branding must run first.
Sadly, that still didn’t work. The Autopilot Branding app was failing, and as a result the Update OS app wouldn’t install (since the dependency is missing). After adding an additional command line option to the Autopilot Branding app to get a verbose MSI log (“/L*v %TEMP%\AutopilotBranding.log”) I could see the failure was happening on the .NET Framework installation. For this round of testing, my device was connected (via Always On VPN, not that it matters) to the corporate network, and therefore it was able to receive the AD GPOs, including a policy that pointed the Windows Update service to my ConfigMgr server. But that won’t work for .NET Framework, as it needs to get those components from the internet.
So the next step was to fix that problem: Since you can’t install features from WSUS, I added logic to the AutopilotBranding.ps1 script to turn off WSUS, restart the Windows Update service, install .NET (or any other specified optional components), turn WSUS back on, and then restart the Windows Update service again. I built a new MSI and manually tried it out on an AD-joined device and it worked fine. So I packaged that MSI into a new .INTUNEWIN file, uploaded it, and tried it again.
This time, it failed on something brand new: The Autopilot Branding app appeared to install, but it wasn’t detected by Intune so then it never ran the Update OS app. Since I rebuilt the MSI, it got a new MSI product ID, which messed up the existing detection rule. OK, time for a new diversion, getting rid of the MSI app altogether and instead just running the PowerShell script directly as part of a Win32 app, just like the Update OS app does.
Finally, we’re getting somewhere: The Autopilot Branding app installed successfully, and .NET Framework was added without issue as a new feature, pulled from Windows Update (even though the device was configured to talk to WU). The Update OS app then also installed successfully, installing all detected updates successfully, including KB4556799. The Update OS app then specified a return code of 3010 to indicate that the enrollment status page (ESP) should reboot when it finishes with the device ESP process.
After the reboot, everything was configured as expected: .NET installed, customizations applied, latest updates installed. Everything looks great. (And that leaves out various additional attempts that I made – that’s the simplified version of the process I went through.)
Setting up Autopilot Branding as a Win32 app
All the changes to the Autopilot Branding files have been checked into the GitHub repo. So download them or clone the repository, make your changes, and then run “makeapp.cmd” to build the AutopilotBranding.intunewin file that you need to set up the app. Once you have that file, you can sign into the Intune portal and create a new Win32 app:
Next, browse to your AutopilotBranding.intunewin file.
Specify an appropriate name, description and publisher.
Specify the program details:
Install command: powershell.exe -noprofile -executionpolicy bypass -file .\AutopilotBranding.ps1
Uninstall command: cmd.exe /c del %ProgramData%\Microsoft\AutopilotBranding\AutopilotBranding.ps1.tag
Device restart behavior: Determine behavior based on return codes
Specify both x86 and x64 for requirements (another benefit of not using an MSI, which is architecture-specific), and select Windows 10 1903 and above for OS version.
Specify a detection rule that looks for the file that the script creates.
No dependencies are needed. Assign to a device group as desired (I used “All devices”).
Then create the app.
Note that I haven’t updated the documentation in GitHub – that came come later.
Setting up Update OS as a Win32 app
The original blog has all the needed details – make sure you download or sync the latest version of the script to get the logic changes previously mentioned. The GitHub repository also has an abbreviated set of instructions (basically, the same steps as above, just filling in different values).
Since the Update OS app will report a return code 3010, ESP will automatically reboot the computer at the end of the device setup phase. So by the time the user signs in, you can check WINVER to see that the latest patch is indeed installed. Note that this is going to add some time to the provisioning process, probably 15-30 minutes depending on the speed of the device.
If you really want to get updates from WSUS instead of from WU (and introduce a dependency on needing to connect to your corporate network), you can modify the UpdateOS.ps1 script yourself to remove the -WindowsUpdate switch on the Get-WindowsUpdate command. But I wouldn’t recommend it. If you do white glove deployments, or even VPN-driven deployments in the future, the Update OS app may not have the connectivity needed at the right point in the process.
Setting up the Dependency
You can add a dependency to the Update OS app, saying that the Autopilot Branding app is a dependency and should automatically be installed. Intune will then take care of the process.
Putting together individual building blocks (scripts in this case) always seems simple enough, until you put them into more “real world” scenarios and combine them together into what should be repeatable processes. Try it out yourself, and let me know if you find any issues (test accordingly, don’t blame me if you deploy it without testing and then find the problem).
Categories: Windows Autopilot