During this blog series, I’ve got lot’s of comments and DMs on Twitter, how could we automatise the OOBE part. Even some of my customers have wished the same: “why should we wait until OOBE and execute some scripts manually? We would like to have a fully zero touch deployment.“
Sure, I can understand your needs, but until now the OSDCloud process stops for an authentication anyway (for Autopilot registration). So, somebody has to give his credentials to continue the process. In my opinion, it’s not a big deal to kick off a script manually and wait some minutes for user authentication. But anyway… the business drives the process 🙂
In this post, I am showing you how I was able to manage the first half of a 100% automated OSD process without having any On-Premise infrastructure. The last part regarding Autopilot part is still coming.
The challenge was to bypass the process, after the OOBE phase was starting. If you are not familiar until that point, how the OSDCloud peaces were coming together, please check my previous posts. If you are not familiar in general with the different OSD stages, please google it or ask ChatGPT. 😉
Challenge #1: after having a success OS installation, what should trigger the following OOBE scripts?
I have had more ideas and options:
- Put a pre-defined PPKG file on an USB stick? The OOBE process would grab this and would execute. If we keep the OSDCloud stick plugged in, it would work. But we have here a BIOS boot order dependency. It should be set correctly. It’s not a responsibility of OSDCloud.
- Having a scheduled task which is looking for an event? When we activate the audit policies, we could grab an event id, which would say, we are in the OOBE phase (WWAHost.exe is running).
- Having a scheduled task which is running when an user logs in? But which user? For sure, our best friend in this phase, DEFAULTUSER0.
–> I took the last option: we create a scheduled task which is running if any user logs in.
But still, our machine is in a kind of debug mode and we are not seeing any process which running in the background.
Challenge #2: how can we see what is going on in the backend?
We have to stop this debug mode –> send keystrokes (Shift + F10) to the active window as if they were typed at the keyboard. This method is similar to the VB SendKeys method, WscriptShell.SendKeys.
Challenge #3: how can we have interaction between session 0 and session 1?
Who is already working with PSADT, or using MDT, ConfigMgr, knows the answer –> use ServiceUI.exe from the Microsoft deployment toolkit. These scheduled tasks are running in System context, but we want to see the results and the progress in the user context too.
Challenge #4: how do we create this scheduled tasks?
In the WinPE phase we are not able to create the scheduled tasks. The required PowerShell commands are still not available in this phase and the COM object (Schedule.Service) isn’t ready yet to use it –> let’s use for this purpose the SetupComplete.cmd.
The https://oobetasks.osdcloud.ch script has some comments where I put some information in it too for a better understanding.
The whole OSDCloud script can be found here. The old OOBE commands are still there, but we don’t have to execute the OOBE.cmd script. If you are good enough in script puzzling (some copy & paste work), either the SetupComplete.cmd does the job for you. 😉
Please feel free to test and use these scripts and let me know how it fits to your requirements.
Happy OSDClouding!
Permalink
I have a challenge for the community.
For those of you trying to do this all via WiFi, is it possible to automate the connection at the start of OOBE using the WiFi.json file that was created in the WinPE phase.
Relevant code can be found here: https://github.com/OSDeploy/OSD/blob/master/Public/Functions/OSDCloud/Invoke-OSDSpecialize.ps1#L207-L214
Permalink
When and how should i run W11_OOBEcmd.ps1? Is it in Startnet.cmd? Please explain the flow here 🙂
Permalink
Please read this post throw and you can find the answer 😉
https://akosbakos.ch/osdcloud-3-zero-touch-deployment/
One of the keyword here is “StartOSDPad”
Permalink
When running your ZTI process, I get a warning with Start-AutopilotOOBE : “The register button is disabled when the UserName is not defaultuser0”
I’m unable to click the register button in the GUI
Permalink
If you are running this script via (by me designed) scheduled task, you are SYSTEM at this point & don’t defaultuser0, which the commandlet requires.
Permalink
Hi Akos,
Is there anyway around this at all? Everything is perfect until this point for me.
Permalink
Do you mean triggering the OOBE tasks automatically?
I am using this solution: oobetasks.osdcloud.ch
And in the post below, you can find the SetupComplete.cmd which is creating these scheduled tasks in the specialize phase.
Permalink
Hi again,
This the error I get, as that the OP:
When running your ZTI process, I get a warning with Start-AutopilotOOBE : “The register button is disabled when the UserName is not defaultuser0”
I’m unable to click the register button in the GUI
I used your script that you referenced, everything works apart from the register button not being clickable.
Permalink
Hi Akos
Instead of using Task Scheduler, you can add your both scripts “SendKeys” and “OOBE” in local Logonscript folder. (C:\WINDOWS\System32\GroupPolicy\User\Scripts\Logon) In this way AutoPilot registration can be run as “defaultuser0” and the button will be acitv.
Then you have to add some additional “.ini”s.
$psscriptsini = @”
`
[Logon]
0CmdLine=SendKeys.ps1
0Parameters=
1CmdLine=OOBE.ps1
1Parameters=
“@
Out-File -FilePath “C:\WINDOWS\System32\GroupPolicy\User\Scripts\psscripts.ini” -InputObject $psscriptsini -Encoding ascii -Force
$gpt = @”
`[General]
gPCUserExtensionNames=[{42B5FAAE-6536-11D2-AE5A-0000F87571E3}{40B66650-4972-11D1-A7CA-0000F87571E3}]
Version=65536
“@
Out-File -FilePath “C:\WINDOWS\System32\GroupPolicy\gpt.ini” -InputObject $gpt -Encoding ascii -Force
Set-ItemProperty -Path “C:\WINDOWS\System32\GroupPolicy” -Name Attributes -Value ([System.IO.FileAttributes]::Hidden)
Set-ItemProperty -Path “C:\WINDOWS\System32\GroupPolicy\User\Scripts\psscripts.ini” -Name Attributes -Value ([System.IO.FileAttributes]::Hidden)
Permalink
I know this is more static, but if i have local powershell scripts i want to run with SetupComplete, how do i do that? Not having them in uploaded to Github.
Lets say i have three powershell scripts i my WinPE image.
I want to copy them to the C-drive and runt them in OOBE.
Permalink
You already have the solution. 😉
You must just code it, like this:
Copy-Item X:\OSDCloud\Scripts C:\OSDCloud\ -Recurse -Force
$SetupCompleteCMD = @’
powershell.exe -Command Set-ExecutionPolicy RemoteSigned -Force
powershell.exe -File “C:\OSDCloud\xxx.ps1”
‘@
$SetupCompleteCMD | Out-File -FilePath ‘C:\Windows\Setup\Scripts\SetupComplete.cmd’ -Encoding ascii -Force
Permalink
Thanks Akos for the Amazon series on OSSCLOUD, I understand the workflow you are trying to explain in this but just wondering how to call the script in the OOBE phase
I am setup the Zero touch deployment but can’t get my head around what to do next, my company doesn’t allow hosting anything on GitHub so that’s not an option
Any feedback would be appreciated
Permalink
That’s a bit the tricky part. In the specialize phase, two scheduled tasks have been created which are getting triggered in the OOBE phase.
During my solutions, I am always referencing for an online script repository (GitHub/Azure), but these ones can work for sure with local scripts as well. You should import them into the boot.wim and in the WinPE phase care about for the copy job into C:\.
PS: I think we could have here a better and easier way, but so far I am still happy with this solution.
Permalink
Thanks Akos for the tip 😀
I think I can figure that part out
Thanks again for the amazing series
Permalink
For any one interested in running a PS Script AUTOMATICALLY right at the start of OOBE here is how do this. I just did for Win 11 Hyper-V VM and it worked
1- Create the Setupcomplete.cmd file using the following
#================================================
# [PostOS] SetupComplete CMD Command Line
#================================================
Write-Host -ForegroundColor Green “Create C:\Windows\Setup\Scripts\SetupComplete.cmd”
$SetupCompleteCMD = @’
powershell.exe -Command Set-ExecutionPolicy RemoteSigned -Force
powershell.exe -Command “& {IEX (IRM oobetasks.osdcloud.ch)}”
‘@
$SetupCompleteCMD | Out-File -FilePath ‘C:\Windows\Setup\Scripts\SetupComplete.cmd’ -Encoding ascii -Force
2- Install Windows System Image Manager as part of ADK (Downlaod ADK from MS official site as an MSI and install the MSI which will put Windows System image manager in your start menu)
3- Open Windows System Image Manager and create a new answer file (unattend.xml)
4- Open your WIM file (if you do not have a WIM file I converted ISO to VHDX then I captured the VHDX into a WIM)
5- Add RunsynchronousCommand to phase 4 which is the specialize phase
6- in the RunsynchronousCommand path add cmd /c C:\Windows\Setup\Scripts\SetupComplete.cmd for the command path so your unattend.xml should look like this
cmd /c C:\Windows\Setup\Scripts\SetupComplete.cmd
1
install autopilot oobe
7- save the unattend.xml at the root of the VHDX or pass it to your ISO/WIM2VHDX script in the unttend.xml parameter here is the script https://raw.githubusercontent.com/x0nn/Convert-WindowsImage/main/Convert-WindowsImage.ps1
8- then create a new Hyper-V and use the VHDX from step 7 as a parent for a differencing disk so that way your VM boot directly into the specialize phase skipping windows install
9- in the specialize phase you will a cmd window pop up which will run the setupcomplete.cmd specified in the unattend.xml specialize phase
10- then the VM will reboot and then come to the OOBE screen and within 15 seconds the scheduled tasks will automatically kick in and start running your PowerShell scripts
the rest is just whatever automation you want