OSDCloud #9 – OOBE challenges

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!


  1. When and how should i run W11_OOBEcmd.ps1? Is it in Startnet.cmd? Please explain the flow here 🙂


  2. 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


    1. 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.


      1. Hi Akos,

        Is there anyway around this at all? Everything is perfect until this point for me.


        1. 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.


          1. 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.

  3. 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 = @”

    Out-File -FilePath “C:\WINDOWS\System32\GroupPolicy\User\Scripts\psscripts.ini” -InputObject $psscriptsini -Encoding ascii -Force

    $gpt = @”

    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)


  4. 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.


    1. 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


  5. 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


    1. 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.


      1. Thanks Akos for the tip 😀

        I think I can figure that part out

        Thanks again for the amazing series


  6. 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
    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


Leave a Reply

Your email address will not be published. Required fields are marked *