<# .SYNOPSIS OSDCloud Logic secret gist .DESCRIPTION start.homburger.osdcloud.ch .NOTES Version: 0.1 Creation Date: 17.05.2025 Author: Akos Bakos Company: SmartCon GmbH Contact: akos.bakos@smartcon.ch Copyright (c) 2025 SmartCon GmbH HISTORY: Date By Comments ---------- --- ---------------------------------------------------------- 17.05.2025 Akos Bakos Added profile selection logic 02.06.2025 Akos Bakos Modified $AssignedComputerName logic for PCs 03.06.2025 Akos Bakos Added OOBETasks for PCs 03.06.2025 Akos Bakos Change OSEdition to Enterprise 19.08.2025 Akos Bakos STAP profile setup 29.08.2025 Akos Bakos Shared Geraet profile setup 05.09.2029 Akos Bakos Schulungsraum profile setup 15.09.2025 Akos Bakos Bibliotheks/Transfer PC profile setup 26.09.2025 Akos Bakos Device name is the last 5 character 07.11.2025 Akos Bakos Corrected Transfer-PC and Biblio-PC naming convention 06.02.2026 Akos Bakos Added TransferPC profile #> if (-NOT (Test-Path 'X:\OSDCloud\Logs')) { New-Item -Path 'X:\OSDCloud\Logs' -ItemType Directory -Force -ErrorAction Stop | Out-Null } #Transport Layer Security (TLS) 1.2 [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 #[System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials $Transcript = "$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Start-OSDCloudLogic.log" Start-Transcript -Path (Join-Path "X:\OSDCloud\Logs" $Transcript) -ErrorAction Ignore | Out-Null function Get-UserMenuChoice { $validChoices = @('1', '2', '3', '4', '5', '6') $choiceDescriptions = @{ '1' = 'Office Notebook/PC' '2' = 'Bueronotebook' '3' = 'Shared Geraet' '4' = 'Schulungsraum' '5' = 'Bibliothek PC' '6' = 'Transfer PC' } $isValidChoice = $false $userChoice = $null # Display menu options Write-Host "`nPlease select an option:" -ForegroundColor Cyan Write-Host "1 - Office Notebook/PC" Write-Host "2 - Bueronotebook" Write-Host "3 - Shared Geraet" Write-Host "4 - Schulungsraum" Write-Host "5 - Bibliothek PC" Write-Host "6 - Transfer PC" # Loop until valid input is received while (-not $isValidChoice) { # Get user input $userChoice = Read-Host "`nEnter your choice (1-6)" # Validate input if ($validChoices -contains $userChoice) { $isValidChoice = $true # Confirm the selection with the full option text Write-Host -NoNewline "You selected '" Write-Host -NoNewline -ForegroundColor Yellow "$userChoice - $($choiceDescriptions[$userChoice])" $confirmChoice = Read-Host "'. Is this correct? (Y/N)" if ($confirmChoice -notmatch '^[Yy]') { $isValidChoice = $false Write-Host "Let's try again." -ForegroundColor Green } } else { Write-Host "Invalid choice. Please enter 1, 2, 3, 4, 5, or 6." -ForegroundColor Red } } # Return the validated choice return $userChoice } # Call the function and store the result $selectedOption = Get-UserMenuChoice # Process the selected option switch ($selectedOption) { '1' { Write-Host "`nYou selected Office Notebooks. Proceeding...`n" -ForegroundColor Green } '2' { Write-Host "`nYou selected Bueronotebooks. Proceeding...`n" -ForegroundColor Green } '3' { Write-Host "`nYou selected Shared Geraet. Proceeding...`n" -ForegroundColor Green } '4' { Write-Host "`nYou selected Schulungsraum PC. Proceeding...`n" -ForegroundColor Green } '5' { Write-Host "`nYou selected Bibliothek PC. Proceeding...`n" -ForegroundColor Green } '6' { Write-Host "`nYou selected Transfer PC. Proceeding...`n" -ForegroundColor Green } default { Write-Host "`nInvalid selection. Exiting script.`n" -ForegroundColor Red; exit 1 } } # If option 5 was chosen, prompt for a custom computer name now if ($selectedOption -eq '5') { do { $biblioInput = (Read-Host "Enter the 2-digit number for 'BiblioPC-' or the full name 'BiblioPC-##'").Trim() $isValid = $biblioInput -match '^(?:BiblioPC-)?\d{2}$' if (-not $isValid) { Write-Host "Invalid input. Enter '01'..'99' or 'BiblioPC-01'..'BiblioPC-99'." -ForegroundColor Red } } until ($isValid) if ($biblioInput -match '^\d{2}$') { $AssignedComputerName = "BiblioPC-$biblioInput" } Write-Host "Computer name set to: $AssignedComputerName" -ForegroundColor Yellow } # If option 6 was chosen, prompt for a custom computer name now if ($selectedOption -eq '6') { do { $transferInput = (Read-Host "Enter the 2-digit number for 'TransferPC-' or the full name 'TransferPC-##'").Trim() $isValid = $transferInput -match '^(?:TransferPC-)?\d{2}$' if (-not $isValid) { Write-Host "Invalid input. Enter '01'..'99' or 'TransferPC-01'..'TransferPC-99' (final name max 12 chars)." -ForegroundColor Red } } until ($isValid) if ($transferInput -match '^\d{2}$') { $AssignedComputerName = "TransferPC-$transferInput" } Write-Host "Computer name set to: $AssignedComputerName" -ForegroundColor Yellow } #================================================ Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor DarkGray "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) " -NoNewline Write-Host -ForegroundColor Cyan "[PreOS] Update Module" #================================================ # Write-Host -ForegroundColor Green "Updating OSD PowerShell Module" # Install-Module OSD -Force Write-Host -ForegroundColor DarkGray "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) " -NoNewline Write-Host -ForegroundColor Green "Importing OSD PowerShell Module" Import-Module OSD -Force Write-Host -ForegroundColor DarkGray "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) " -NoNewline Write-Host -ForegroundColor Green "PSCloudScript at functions.osdcloud.com" Invoke-Expression (Invoke-RestMethod -Uri functions.osdcloud.com) #region Helper Functions function Write-DarkGrayDate { [CmdletBinding()] param ( [Parameter(Position = 0)] [System.String] $Message ) if ($Message) { Write-Host -ForegroundColor DarkGray "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) $Message" } else { Write-Host -ForegroundColor DarkGray "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) " -NoNewline } } function Write-DarkGrayHost { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [System.String] $Message ) Write-Host -ForegroundColor DarkGray $Message } function Write-DarkGrayLine { [CmdletBinding()] param () Write-Host -ForegroundColor DarkGray "=========================================================================" } function Write-SectionHeader { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [System.String] $Message ) Write-DarkGrayLine Write-DarkGrayDate Write-Host -ForegroundColor Cyan $Message } function Write-SectionSuccess { [CmdletBinding()] param ( [Parameter(Position = 0)] [System.String] $Message = 'Success!' ) Write-DarkGrayDate Write-Host -ForegroundColor Green $Message } #endregion #region PreOS Tasks #======================================================================= Write-SectionHeader "[PreOS] Define OSDCloud Global And Customer Parameters" #======================================================================= $Global:Homburger = $null $Global:Homburger = [ordered]@{ Development = [bool]$true TestGroup = [bool]$false } Write-SectionHeader "Homburger variables" Write-Host ($Global:Homburger | Out-String) $Global:MyOSDCloud = [ordered]@{ MSCatalogFirmware = [bool]$true HPBIOSUpdate = [bool]$true #IsOnBattery = [bool]$false } Write-SectionHeader "MyOSDCloud variables" Write-Host ($Global:MyOSDCloud | Out-String) if ($Global:OSDCloud.ApplyCatalogFirmware -eq $true) { #======================================================================= Write-SectionHeader "[PreOS] Prepare Firmware Tasks" #======================================================================= #Register-PSRepository -Default -Verbose osdcloud-TrustPSGallery -Verbose #Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -Verbose osdcloud-InstallPowerShellModule -Name 'MSCatalog' #Install-Module -Name MSCatalog -Force -Verbose -SkipPublisherCheck -AllowClobber -Repository PSGallery } #endregion #region OS Tasks #======================================================================= Write-SectionHeader "[OS] Params and Start-OSDCloud" #======================================================================= $Params = @{ OSVersion = "Windows 11" OSBuild = "23H2" OSEdition = "Pro" OSLanguage = "en-us" OSLicense = "Retail" ZTI = $true Firmware = $true } Write-Host ($Params | Out-String) Start-OSDCloud @Params #endregion #region Autopilot Tasks #================================================ Write-SectionHeader "[PostOS] Define Autopilot Attributes" #================================================ Write-DarkGrayHost "Define Computername" $Serial = Get-WmiObject Win32_bios | Select-Object -ExpandProperty SerialNumber $lastFiveChars = $serial.Substring($serial.Length - 5) $ChassisType = (Get-WmiObject -Query "SELECT * FROM Win32_SystemEnclosure").ChassisTypes # Only compute name if not already provided (e.g., for option 5) if (-not $AssignedComputerName) { switch ($selectedOption) { '1' { If ($ChassisType -eq "8" -or # Portable $ChassisType -eq "9" -or # Laptop $ChassisType -eq "10" -or # Notebook $ChassisType -eq "11" -or # Hand Held $ChassisType -eq "12" -or # Docking Station $ChassisType -eq "14" -or # Sub Notebook $ChassisType -eq "18" -or # Expansion Chassis $ChassisType -eq "21" -or # Peripheral Chassis $ChassisType -eq "31") { # Convertible $AssignedComputerName = "NB-2$lastFiveChars" } elseif ($ChassisType -eq "3" -or # Desktop $ChassisType -eq "4" -or # Low Profile Desktop $ChassisType -eq "5" -or # Pizza Box $ChassisType -eq "6" -or # Mini Tower $ChassisType -eq "7" -or # Tower $ChassisType -eq "15" -or # Space-Saving $ChassisType -eq "16" -or # Lunch Box $ChassisType -eq "35") { # Unknown/Custom $AssignedComputerName = "PC-2$lastFiveChars" } else { Write-Host "Unknown chassis type: $ChassisTypes" -ForegroundColor Red $AssignedComputerName = "RENAME-$lastFiveChars" } } '2' { $AssignedComputerName = "BN-2$lastFiveChars" } '3' { $AssignedComputerName = "Shared-2$lastFiveChars" } '4' { $AssignedComputerName = "SR-2$lastFiveChars" } default { Write-Host "Default naming convention for any other options" -ForegroundColor Red $AssignedComputerName = "NB-2$lastFiveChars" } } } else { Write-Host "Using provided computer name: $AssignedComputerName" -ForegroundColor Yellow } # Device assignment switch ($selectedOption) { '1' { # Office Notebooks group tag if ($Global:Homburger.TestGroup -eq $true) { $AddToGroup = "AZ_COM_TST_HMB" } else { $AddToGroup = "AZ_COM_PRD_HMB" } } '2' { # Büronotebooks group tag $AddToGroup = "AZ_COM_BN_HMB" } '3' { # Shared Geraet group tag $AddToGroup = "AZ_COM_SHARED_HMB" } '4' { # Schulungsraum group tag $AddToGroup = "AZ_COM_SR_HMB" } '5' { # Bibliotheks PC group tag $AddToGroup = "AZ_COM_BIB_HMB" } '6' { # Transfer PC group tag $AddToGroup = "AZ_COM_TRANSFER_HMB" } default { Write-Host "Default group tag for any other options" -ForegroundColor Red $AddToGroup = "AZ_COM_PRD_HMB" } } Write-Host "Adding device to group: $AddToGroup" -ForegroundColor Yellow #================================================ Write-SectionHeader "[PostOS] AutopilotOOBE Configuration" #================================================ Write-DarkGrayHost "Create C:\ProgramData\OSDeploy\OSDeploy.AutopilotOOBE.json file" $AutopilotOOBEJson = @" { "AssignedComputerName" : "$AssignedComputerName", "AddToGroup": "$AddToGroup", "Assign": { "IsPresent": true }, "GroupTag": "$GroupTag", "Hidden": [ "AddToGroup", "AssignedUser", "PostAction", "GroupTag", "Assign" ], "PostAction": "Quit", "Run": "NetworkingWireless", "Docs": "https://google.com/", "Title": "Autopilot Manual Register" } "@ If (!(Test-Path "C:\ProgramData\OSDeploy")) { New-Item "C:\ProgramData\OSDeploy" -ItemType Directory -Force | Out-Null } $AutopilotOOBEJson | Out-File -FilePath "C:\ProgramData\OSDeploy\OSDeploy.AutopilotOOBE.json" -Encoding ascii -Force #endregion #region Specialize Tasks #================================================ Write-SectionHeader "[PostOS] SetupComplete CMD Command Line" #================================================ Write-DarkGrayHost "Cleanup SetupComplete Files from OSDCloud Module" Get-ChildItem -Path 'C:\Windows\Setup\Scripts\SetupComplete*' -Recurse | Remove-Item -Force #================================================= Write-SectionHeader "[PostOS] Define Specialize Phase" #================================================= $UnattendXml = @' 1 Start Autopilot Import & Assignment Process PowerShell -ExecutionPolicy Bypass C:\Windows\Setup\scripts\autopilot.ps1 de-CH de-DE de-DE de-CH '@ # Get-OSDGather -Property IsWinPE Block-WinOS if (-NOT (Test-Path 'C:\Windows\Panther')) { New-Item -Path 'C:\Windows\Panther'-ItemType Directory -Force -ErrorAction Stop | Out-Null } $Panther = 'C:\Windows\Panther' $UnattendPath = "$Panther\Unattend.xml" $UnattendXml | Out-File -FilePath $UnattendPath -Encoding utf8 -Width 2000 -Force Write-DarkGrayHost "Use-WindowsUnattend -Path 'C:\' -UnattendPath $UnattendPath" Use-WindowsUnattend -Path 'C:\' -UnattendPath $UnattendPath | Out-Null #endregion #region OOBE Tasks #================================================ Write-SectionHeader "[PostOS] OOBE CMD Command Line" #================================================ Write-DarkGrayHost "Downloading Scripts for OOBE and specialize phase" Invoke-RestMethod http://autopilot.homburger.osdcloud.ch | Out-File -FilePath 'C:\Windows\Setup\scripts\autopilot.ps1' -Encoding ascii -Force #Invoke-RestMethod http://gist.githubusercontent.com/homburgerag/ae093cd0a6e413556f6c80ad6d49ac7f/raw/AutopilotV2.ps1 | Out-File -FilePath 'C:\Windows\Setup\scripts\autopilot.ps1' -Encoding ascii -Force Invoke-RestMethod http://oobedeploy.homburger.osdcloud.ch | Out-File -FilePath 'C:\Windows\Setup\scripts\oobe.ps1' -Encoding ascii -Force Invoke-RestMethod http://cleanup.homburger.osdcloud.ch | Out-File -FilePath 'C:\Windows\Setup\scripts\cleanup.ps1' -Encoding ascii -Force Invoke-RestMethod http://osdgather.homburger.osdcloud.ch | Out-File -FilePath 'C:\Windows\Setup\scripts\osdgather.ps1' -Encoding ascii -Force $OOBEcmdTasks = @' @echo off REM Wait for Network 10 seconds REM ping 127.0.0.1 -n 10 -w 1 >NUL 2>&1 REM Execute OOBE Tasks start /wait powershell.exe -NoL -ExecutionPolicy Bypass -F C:\Windows\Setup\Scripts\oobe.ps1 REM Execute OSD Gather Script start /wait powershell.exe -NoL -ExecutionPolicy Bypass -F C:\Windows\Setup\Scripts\osdgather.ps1 REM Execute Cleanup Script start /wait powershell.exe -NoL -ExecutionPolicy Bypass -F C:\Windows\Setup\Scripts\cleanup.ps1 REM Below a PS session for debug and testing in system context, # when not needed REM start /wait powershell.exe -NoL -ExecutionPolicy Bypass '@ # Add reboot command for PCs if ($AssignedComputerName -like 'PC-2*') { $OOBEcmdTasks += @' REM Reboot the system after tasks are completed echo Rebooting the system in 10 seconds... timeout /t 10 shutdown /r /t 0 '@ } else { $OOBEcmdTasks += @' exit '@ } # Then continue with your existing code to write the file if ($AssignedComputerName -like 'PC-2*' -or $AssignedComputerName -like 'Shared-2*') { Write-Host "Create C:\Windows\System32\Homburger.cmd for manually execution in the (user-interactive) OOBE phase" -ForegroundColor Yellow $OOBEcmdTasks | Out-File -FilePath 'C:\Windows\System32\Homburger.cmd' -Encoding ascii -Force } else { Write-Host "Create C:\Windows\Setup\scripts\oobe.cmd for autoamtically execution in the OOBE phase" -ForegroundColor Yellow $OOBEcmdTasks | Out-File -FilePath 'C:\Windows\Setup\scripts\oobe.cmd' -Encoding ascii -Force } Write-DarkGrayHost "Copying PFX file" Copy-Item X:\OSDCloud\Config\Scripts C:\OSDCloud\ -Recurse -Force #endregion Write-DarkGrayHost "Disabling Shift+F10 in OOBE for security Reasons" $Tagpath = "C:\Windows\Setup\Scripts\DisableCMDRequest.TAG" New-Item -ItemType file -Force -Path $Tagpath | Out-Null Write-DarkGrayHost "Shift F10 disabled now!" #region Development if ($Global:Homburger.Development -eq $true -or $AssignedComputerName -like 'PC-2*' -or $AssignedComputerName -like 'Shared-2*') { Write-Host "Enabling Shift+F10 back in OOBE for DEVELOPMENT mode and for PCs" -ForegroundColor Yellow $Tagpath = "C:\Windows\Setup\Scripts\DisableCMDRequest.TAG" Remove-Item -Force -Path $Tagpath | Out-Null Write-Host "Shift F10 enabled now!" -ForegroundColor Yellow Write-Host "Disable Cursor Suppression" -ForegroundColor Yellow #cmd.exe /c reg load HKLM\Offline c:\windows\system32\config\software & cmd.exe /c REG ADD "HKLM\Offline\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableCursorSuppression /t REG_DWORD /d 0 /f & cmd.exe /c reg unload HKLM\Offline Invoke-Exe cmd.exe -Arguments "/c reg load HKLM\Offline c:\windows\system32\config\software" | Out-Null New-ItemProperty -Path HKLM:\Offline\Microsoft\Windows\CurrentVersion\Policies\System -Name EnableCursorSuppression -Value 0 -Force | Out-Null #Invoke-Exe cmd.exe -Arguments "/c REG ADD 'HKLM\Offline\Microsoft\Windows\CurrentVersion\Policies\System' /v EnableCursorSuppression /t REG_DWORD /d 0 /f " Invoke-Exe cmd.exe -Arguments "/c reg unload HKLM\Offline" | Out-Null } #endregion #======================================================================= Write-SectionHeader "Moving OSDCloud Logs to IntuneManagementExtension\Logs\OSD" #======================================================================= if (-NOT (Test-Path 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\OSD')) { New-Item -Path 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\OSD' -ItemType Directory -Force -ErrorAction Stop | Out-Null } Get-ChildItem -Path X:\OSDCloud\Logs\ | Copy-Item -Destination 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\OSD' -Force if ($Global:Homburger.Development -eq $false) { Write-DarkGrayHost "Restarting in 20 seconds!" Start-Sleep -Seconds 20 wpeutil reboot Stop-Transcript | Out-Null } else { Write-DarkGrayHost "Development Mode - No reboot!" Stop-Transcript | Out-Null }