I had previously written about OOBE challenges here, and the solution I found was somewhat of a workaround.
Some of my customers are satisfied with these scheduled tasks, but I’ve received comments on the blog and on Twitter that this flow is not very reliable.
I started thinking about a better way to execute the process in the OOBE phase during OSD. For testing purposes, I created a simple batch file in the C:\Windows\Setup\Scripts folder, where SetupComplete.cmd is also located. And guess what? It was executed before the (real) OOBE phase began, without using any scheduled task or ServiceUI.exe.
Note: for me, the real OOBE phase is where the region and keyboard settings have to be selected.
Below, you can find a simple way to trigger your tasks after OS installation and after the specialize phase. Once you understand the solution, everything becomes simple and easy. 😉
Write-Host -ForegroundColor Green "Downloading and creating script for OOBE phase"
Invoke-RestMethod https://raw.githubusercontent.com/AkosBakos/OSDCloud/main/Set-KeyboardLanguage.ps1 | Out-File -FilePath 'C:\Windows\Setup\scripts\keyboard.ps1' -Encoding ascii -Force
Invoke-RestMethod https://raw.githubusercontent.com/AkosBakos/OSDCloud/main/Install-EmbeddedProductKey.ps1 | Out-File -FilePath 'C:\Windows\Setup\scripts\productkey.ps1' -Encoding ascii -Force
Invoke-RestMethod https://check-autopilotprereq.osdcloud.ch | Out-File -FilePath 'C:\Windows\Setup\scripts\autopilotprereq.ps1' -Encoding ascii -Force
Invoke-RestMethod https://start-autopilotoobe.osdcloud.ch | Out-File -FilePath 'C:\Windows\Setup\scripts\autopilotoobe.ps1' -Encoding ascii -Force
$OOBECMD = @'
@echo off
# Execute OOBE Tasks
start /wait powershell.exe -NoL -ExecutionPolicy Bypass -F C:\Windows\Setup\Scripts\keyboard.ps1
start /wait powershell.exe -NoL -ExecutionPolicy Bypass -F C:\Windows\Setup\Scripts\productkey.ps1
start /wait powershell.exe -NoL -ExecutionPolicy Bypass -F C:\Windows\Setup\Scripts\autopilotprereq.ps1
start /wait powershell.exe -NoL -ExecutionPolicy Bypass -F C:\Windows\Setup\Scripts\autopilotoobe.ps1
# Below a PS session for debug and testing in system context, # when not needed
# start /wait powershell.exe -NoL -ExecutionPolicy Bypass
exit
'@
$OOBECMD | Out-File -FilePath 'C:\Windows\Setup\scripts\oobe.cmd' -Encoding ascii -Force
After using this method, don’t forget to cleanup this C:\Windows\Setup\scripts folder like this:
Remove-Item C:\Windows\Setup\Scripts\*.* -Exclude *.TAG -Force | Out-Null
I hope you are disabling shift + F10 during the OSD phase with this simply file: C:\Windows\Setup\Scripts\DisableCMDRequest.TAG. More details please find Michael’s great blog post.
Bonus: please note, if you are working and executing scripts in the OOBE phase, the process will automatically create a new user, defaultuser0. The scripts run in this user’s context.
After the OSD finishes, this account should be removed. You can find a remediation script in my GitHub repo. KUDOS to Ermanno Goletto for the cleanup function.
Please leave a comment about how easy or easier this method is compared to creating two scheduled tasks.
Permalink
So its like your GitHub File?: https://github.com/AkosBakos/OSDCloud/blob/7b28d962e187f9264d6817d209a887ce53d293b2/ScriptPad/W11_simplyfied_OOBE.cmd
I tried it at the script runs before the OOBE phase, the machine reboots and then the normal OOBE is showing up like here (https://i.ibb.co/DVCVRx6/oobe.png). Am I missing something?
Permalink
Any logs in this folder?
$env:ProgramData\Microsoft\IntuneManagementExtension\Logs\OSD\
Permalink
Yes there are this 3 Logfiles:
1) Set-KeyboardLanguage.log
2) Install-EmbeddedProductKey.log
3) Check-AutopilotPrerequisites.log
1 + 2 looks good.
#3 (Check-AutopilotPrerequisites.log)
Shows this in the beginning:
######################################
# Start Autopilot prerequisite check #
######################################
———————————
| Device information |
———————————
Get-ItemPropertyValue : Cannot find path ‘HKLM:\SOFTWARE\Microsoft\Provisioning\AutopilotPolicyCache’ because it does
not exist.
At C:\Program Files\WindowsPowerShell\Scripts\Check-AutopilotPrerequisites.ps1:57 char:23
+ … ilotCache = Get-ItemPropertyValue -Path “HKLM:\SOFTWARE\Microsoft\Pro …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (HKLM:\SOFTWARE\…ilotPolicyCache:String) [Get-ItemPropertyValue],
ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemPropertyValueCommand
Get-ItemPropertyValue : Cannot find path ‘HKLM:\SOFTWARE\Microsoft\Provisioning\AutopilotPolicyCache’ because it does
not exist.
At C:\Program Files\WindowsPowerShell\Scripts\Check-AutopilotPrerequisites.ps1:57 char:23
+ … ilotCache = Get-ItemPropertyValue -Path “HKLM:\SOFTWARE\Microsoft\Pro …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (HKLM:\SOFTWARE\…ilotPolicyCache:String) [Get-ItemPropertyValue], Item
NotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemPropertyValueCommand
All other tests are good
Permalink
That looks good. Meaning, that the scripts have been executed successfully.
Now you can debug each for these scripts what they are doing wrong and/or which scripts you really need in this phase.
Permalink
I have the oobe.cmd placed in the Setup\Script folder, after OCD does its thing it boots up to the AutopilotOOBE Gui but you can’t click the Register button as it states that it needs to run as default0 user which does not exist. If I put the oobe.cmd somewhere else and use Shift/F10 and run it manually it works just fine. I must be missing something as I thought it would trigger the Gui automatically and be able to register
Permalink
Nope, you are right: executing OOBE.cmd like this blog post said, we are SYSTEM.
If you (manually) click Shift+F10, we are DefaultUserX.
You can automate the AutopilotOOBE part with the well-known Get-WindowsAutopilotInfo and/or with the Get-WindowsAutopilotInfoCOMMUNITY version.
Permalink
If you look at the Get-WindowsAutopilotInfo scriot, you realize that there is only a couple of infos you need. Get the hash, get the serial and the product key (empty) and use a group tag. With this infos you will be able to automate the registration using an Azure Function. No GUI or user interacton at all.
Permalink
Hi Akos,
You still have to run oobe.cmd from SetupComplete.cmd correct?
Or do you mean that you can put other .cmd files in the Scripts folder and those files will also run?
Permalink
Hi Johan,
these two scripts are complete separately.
First, the SetupComplete.cmd will be executed and in the next OSD stage comes the OOBE.cmd.