Public/OSDCloud/OSDCloudTemplate.ps1
|
function Get-OSDCloudTemplate { <# .SYNOPSIS Gets information returned by Get-OSDCloudTemplate. .DESCRIPTION Provides the implementation for Get-OSDCloudTemplate. .EXAMPLE Get-OSDCloudTemplate Runs Get-OSDCloudTemplate with common parameters. .NOTES Author: David Segura - Recast Software 2026-07-09 - Updated comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param () #region Block Block-WinPE $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $CurrentPrincipal = [Security.Principal.WindowsPrincipal]::new($CurrentIdentity) if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrative rights are required" return } Block-WindowsVersionNe10 Block-PowerShellVersionLt5 #endregion #region template.json if (Test-Path "$env:ProgramData\OSDCloud\template.json") { $TemplateSettings = Get-Content -Path "$env:ProgramData\OSDCloud\template.json" | ConvertFrom-Json $TemplatePath = $TemplateSettings.TemplatePath if (! (Test-Path "$TemplatePath\Media\sources\boot.wim")) { $TemplatePath = "$env:ProgramData\OSDCloud" $null = Remove-Item -Path "$env:ProgramData\OSDCloud\template.json" -Force } } else { $TemplatePath = "$env:ProgramData\OSDCloud" } #endregion #region Incomplete Template if (! (Test-Path "$TemplatePath\Media\sources\boot.wim")) { Return $null } #endregion #return TemplatePath if (Test-Path "$env:ProgramData\OSDCloud\template.json") { $TemplateSettings = Get-Content -Path "$env:ProgramData\OSDCloud\template.json" | ConvertFrom-Json $TemplatePath = $TemplateSettings.TemplatePath } $TemplatePath #endregion } function Get-OSDCloudTemplateNames { <# .SYNOPSIS Gets information returned by Get-OSDCloudTemplateNames. .DESCRIPTION Provides the implementation for Get-OSDCloudTemplateNames. .EXAMPLE Get-OSDCloudTemplateNames Runs Get-OSDCloudTemplateNames with common parameters. .NOTES Author: David Segura - Recast Software 2026-07-09 - Updated comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param () #region Block Block-WinPE $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $CurrentPrincipal = [Security.Principal.WindowsPrincipal]::new($CurrentIdentity) if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrative rights are required" return } Block-WindowsVersionNe10 Block-PowerShellVersionLt5 #endregion #region Build Array $Results = @() [System.Array]$Results = 'default' [System.Array]$Results += Get-ChildItem -Path "$env:ProgramData\OSDCloud\Templates" | Where-Object { $_.PsIsContainer -eq $true } | Select-Object -ExpandProperty Name #region OSDeploy Compatibility if (Test-Path "$env:ProgramData\OSDeploy\MyBootMedia") { [System.Array]$Results += Get-ChildItem -Path "$env:ProgramData\OSDeploy\MyBootMedia" | Where-Object { $_.PsIsContainer -eq $true } | Select-Object -ExpandProperty Name } if (Test-Path "$env:ProgramData\OSDeploy\WinPEBootMedia") { [System.Array]$Results += Get-ChildItem -Path "$env:ProgramData\OSDeploy\WinPEBootMedia" | Where-Object { $_.PsIsContainer -eq $true } | Select-Object -ExpandProperty Name } #endregion [System.Array]$Results #endregion } function New-OSDCloudTemplate { <# .SYNOPSIS Creates resources by using New-OSDCloudTemplate. .DESCRIPTION Provides the implementation for New-OSDCloudTemplate. .PARAMETER Name Specifies the value for Name. .PARAMETER Language Specifies the value for Language. .PARAMETER CumulativeUpdate Specifies the value for CumulativeUpdate. .PARAMETER SetAllIntl Specifies the value for SetAllIntl. .PARAMETER SetInputLocale Specifies the value for SetInputLocale. .PARAMETER WinRE Indicates whether to enable WinRE. .PARAMETER Add7Zip Indicates whether to enable Add7Zip. .EXAMPLE New-OSDCloudTemplate -Name <Name> -Language <Language> Runs New-OSDCloudTemplate with common parameters. .NOTES Author: David Segura - Recast Software 2026-07-09 - Updated comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param ( [System.String] #Name of the OSDCloud Template. This determines the OSDCloud Template Path $Name = 'default', [ValidateSet ( '*','ar-sa','bg-bg','cs-cz','da-dk','de-de','el-gr', 'en-gb','es-es','es-mx','et-ee','fi-fi', 'fr-ca','fr-fr','he-il','hr-hr','hu-hu','it-it', 'ja-jp','ko-kr','lt-lt','lv-lv','nb-no','nl-nl', 'pl-pl','pt-br','pt-pt','ro-ro','ru-ru','sk-sk', 'sl-si','sr-latn-rs','sv-se','th-th','tr-tr', 'uk-ua','zh-cn','zh-tw' )] [System.String[]] #Adds additional language ADK Packages $Language, [ValidateScript({ if (-Not ($_ | Test-Path)) { throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] CumulativeUpdate must be a valid path to a file." } if (-Not ($_ | Test-Path -PathType Leaf)) { throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] CumulativeUpdate parameter must be a file, not a folder." } if ($_ -notmatch "(\.cab|\.msu)") { throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] CumulativeUpdate must be a .cab or .msu file." } return $true })] [System.IO.FileInfo] #Installs the specified Cumulative Update Package $CumulativeUpdate, [System.String] #Sets all International settings in WinPE to the specified setting $SetAllIntl, [System.String] #Sets the default InputLocale in WinPE to the specified Input Locale $SetInputLocale, [System.Management.Automation.SwitchParameter] #Uses Windows 10 WinRE.wim instead of the ADK Boot.wim $WinRE, [System.Management.Automation.SwitchParameter] #Adds 7Zip to Boot Image $Add7Zip ) #================================================= # WinREDriver #================================================= $WinREDriver = @' [Version] Signature = "$WINDOWS NT$" Class = System ClassGuid = {4D36E97d-E325-11CE-BFC1-08002BE10318} Provider = OSDeploy DriverVer = 07/20/2021,2021.07.20.0 [DefaultInstall] AddReg = AddReg [AddReg] ;rootkey,[subkey],[value],[flags],[data] ;0x00000 REG_SZ ;0x00001 REG_BINARY ;0x10000 REG_MULTI_SZ ;0x20000 REG_EXPAND_SZ ;0x10001 REG_DWORD ;0x20001 REG_NONE HKLM,"Software\Microsoft\Windows NT\CurrentVersion\WinPE",CustomBackground,0x10000,"X:\Windows\System32\winpe.jpg" '@ #================================================= # WinPE Console Registry Settings #================================================= $RegistryConsole = @' Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\Default\Console] "ColorTable00"=dword:000c0c0c "ColorTable01"=dword:00da3700 "ColorTable02"=dword:000ea113 "ColorTable03"=dword:00dd963a "ColorTable04"=dword:001f0fc5 "ColorTable05"=dword:00981788 "ColorTable06"=dword:00009cc1 "ColorTable07"=dword:00cccccc "ColorTable08"=dword:00767676 "ColorTable09"=dword:00ff783b "ColorTable10"=dword:000cc616 "ColorTable11"=dword:00d6d661 "ColorTable12"=dword:005648e7 "ColorTable13"=dword:009e00b4 "ColorTable14"=dword:00a5f1f9 "ColorTable15"=dword:00f2f2f2 "CtrlKeyShortcutsDisabled"=dword:00000000 "CursorColor"=dword:ffffffff "CursorSize"=dword:00000019 "DefaultBackground"=dword:ffffffff "DefaultForeground"=dword:ffffffff "EnableColorSelection"=dword:00000000 "ExtendedEditKey"=dword:00000001 "ExtendedEditKeyCustom"=dword:00000000 "FaceName"="Consolas" "FilterOnPaste"=dword:00000001 "FontFamily"=dword:00000036 "FontSize"=dword:00140000 "FontWeight"=dword:00000000 "ForceV2"=dword:00000000 "FullScreen"=dword:00000000 "HistoryBufferSize"=dword:00000032 "HistoryNoDup"=dword:00000000 "InsertMode"=dword:00000001 "LineSelection"=dword:00000001 "LineWrap"=dword:00000001 "LoadConIme"=dword:00000001 "NumberOfHistoryBuffers"=dword:00000004 "PopupColors"=dword:000000f5 "QuickEdit"=dword:00000001 "ScreenBufferSize"=dword:23290078 "ScreenColors"=dword:00000007 "ScrollScale"=dword:00000001 "TerminalScrolling"=dword:00000000 "TrimLeadingZeros"=dword:00000000 "WindowAlpha"=dword:000000ff "WindowSize"=dword:001e0078 "WordDelimiters"=dword:00000000 [HKEY_LOCAL_MACHINE\Default\Console\%SystemRoot%_System32_cmd.exe] "FaceName"="Consolas" "FilterOnPaste"=dword:00000000 "FontSize"=dword:00100000 "FontWeight"=dword:00000190 "LineSelection"=dword:00000000 "LineWrap"=dword:00000000 "WindowAlpha"=dword:00000000 "WindowPosition"=dword:00000000 "WindowSize"=dword:00110054 [HKEY_LOCAL_MACHINE\Default\Console\%SystemRoot%_System32_WindowsPowerShell_v1.0_powershell.exe] "ColorTable05"=dword:00562401 "ColorTable06"=dword:00f0edee "FaceName"="Consolas" "FilterOnPaste"=dword:00000000 "FontFamily"=dword:00000036 "FontSize"=dword:00140000 "FontWeight"=dword:00000190 "LineSelection"=dword:00000000 "LineWrap"=dword:00000000 "PopupColors"=dword:000000f3 "QuickEdit"=dword:00000001 "ScreenBufferSize"=dword:03e8012c "ScreenColors"=dword:00000056 "WindowAlpha"=dword:00000000 "WindowSize"=dword:0020006c [HKEY_LOCAL_MACHINE\Default\Console\%SystemRoot%_SysWOW64_WindowsPowerShell_v1.0_powershell.exe] "ColorTable05"=dword:00562401 "ColorTable06"=dword:00f0edee "FaceName"="Consolas" "FilterOnPaste"=dword:00000000 "FontFamily"=dword:00000036 "FontSize"=dword:00140000 "FontWeight"=dword:00000190 "LineSelection"=dword:00000000 "LineWrap"=dword:00000000 "PopupColors"=dword:000000f3 "QuickEdit"=dword:00000001 "ScreenBufferSize"=dword:03e8012c "ScreenColors"=dword:00000056 "WindowAlpha"=dword:00000000 "WindowSize"=dword:0020006c '@ #region Initialize $TemplateStartTime = Get-Date Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] $($MyInvocation.MyCommand.Name)" Block-WinPE $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $CurrentPrincipal = [Security.Principal.WindowsPrincipal]::new($CurrentIdentity) if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrative rights are required" return } Block-WindowsVersionNe10 Block-PowerShellVersionLt5 Block-NoCurl #endregion <# Removing ARM64 Support for now #region Get Adk Paths if ($PSBoundParameters.ContainsKey('ARM64')) { $WindowsAdkPaths = Get-WindowsAdkPaths -Architecture 'arm64' } else { $WindowsAdkPaths = Get-WindowsAdkPaths } if ($OSArch -eq 'ARM64') { $WindowsAdkPaths = Get-WindowsAdkPaths -Architecture 'arm64' } else { $WindowsAdkPaths = Get-WindowsAdkPaths } #> $WindowsAdkPaths = Get-WindowsAdkPaths if ($null -eq $WindowsAdkPaths) { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Warning "[$(Get-Date -format s)] Could not get ADK going, sorry" Write-Host -ForegroundColor DarkGray "=========================================================================" Break } #endregion #region Test WinRE if ($PSBoundParameters.ContainsKey('WinRE')) { if ((Get-WinREPartition).OperationalStatus -ne 'Online') { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Warning "[$(Get-Date -format s)] You can't use WinRE because of some issue. Sorry!" Write-Host -ForegroundColor DarkGray "=========================================================================" Break } if ((Get-RegCurrentVersion).CurrentBuild -gt 20000) { #Write-Host -ForegroundColor DarkGray "=========================================================================" #Write-Warning "[$(Get-Date -format s)] Windows 11 WinRE may not support booting some Virtual Machines" #Write-Warning "[$(Get-Date -format s)] It is recommended that you remove the -WinRE parameter if you need Virtual Machine support" #Write-Warning "[$(Get-Date -format s)] Press Ctrl+C to cancel in the next 10 seconds" #Start-Sleep -Seconds 10 #Write-Host -ForegroundColor DarkGray "=========================================================================" } } #endregion #region Test WimSourcePath $WimSourcePath = $WindowsAdkPaths.WimSourcePath if (-NOT (Test-Path $WimSourcePath)) { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Warning "[$(Get-Date -format s)] Could not find the ADK WimSourcePath: $WimSourcePath" Write-Host -ForegroundColor DarkGray "=========================================================================" Break } #endregion #region Template Write-Host -ForegroundColor DarkGray "=========================================================================" if ($Name -eq 'default') { $OSDCloudTemplate = "$env:ProgramData\OSDCloud" } else { $OSDCloudTemplate = "$env:ProgramData\OSDCloud\Templates\$Name" } if (-NOT (Test-Path $OSDCloudTemplate)) { Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Creating OSDCloud Template: $OSDCloudTemplate" $null = New-Item -Path $OSDCloudTemplate -ItemType Directory -Force } #endregion #region Logging $TemplateLogs = "$OSDCloudTemplate\Logs\Template" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Creating OSDCloud Template Logs at $TemplateLogs" if (Test-Path $TemplateLogs) { $null = Remove-Item -Path "$TemplateLogs\*" -Recurse -Force -ErrorAction Ignore | Out-Null } if (-NOT (Test-Path $TemplateLogs)) { $null = New-Item -Path $TemplateLogs -ItemType Directory -Force | Out-Null } $Transcript = "$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-New-OSDCloudTemplate.log" Start-Transcript -Path (Join-Path $TemplateLogs $Transcript) -ErrorAction Ignore #endregion #region Mirror ADK Media Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Mirroring ADK Media using Robocopy" Write-Host -ForegroundColor Yellow 'Mirroring will remove any previous WinPE and will force a full rebuild' $PathWinPEMedia = $WindowsAdkPaths.PathWinPEMedia Write-Host -ForegroundColor DarkGray "Source: $PathWinPEMedia" $DestinationMedia = Join-Path $OSDCloudTemplate 'Media' Write-Host -ForegroundColor DarkGray "Destination: $DestinationMedia" $null = robocopy "$PathWinPEMedia" "$DestinationMedia" *.* /mir /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log #endregion #region Copy Boot.wim #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" $DestinationSources = Join-Path $DestinationMedia 'sources' if (-NOT (Test-Path "$DestinationSources")) { New-Item -Path "$DestinationSources" -ItemType Directory -Force -ErrorAction Stop | Out-Null } if ($PSBoundParameters.ContainsKey('WinRE')) { Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Copying WinRE.wim" Write-Host -ForegroundColor Yellow "OSD Function: Copy-WinREWIM" $BootWim = Join-Path $DestinationSources 'winre.wim' Write-Host -ForegroundColor DarkGray "Destination: $BootWim" Copy-WinREWIM -DestinationDirectory $DestinationSources -DestinationFileName 'winre.wim' -ErrorAction Stop | Out-Null } else { Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Copying ADK WinPE.wim" $BootWim = Join-Path $DestinationSources 'boot.wim' Write-Host -ForegroundColor DarkGray "Source: $WimSourcePath" Write-Host -ForegroundColor DarkGray "Destination: $BootWim" Copy-Item -Path $WimSourcePath -Destination $BootWim -Force -ErrorAction Stop | Out-Null } #endregion #region Test BootWim if (!(Test-Path $BootWim)) { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Warning "I'm not sure what happened, but I can't find $BootWim" Write-Host -ForegroundColor DarkGray "=========================================================================" Break } attrib -s -h -r $DestinationSources attrib -s -h -r $BootWim #endregion #region wgl4_boot.ttf #This is used to resolve issues with WinPE Resolutions in 2004/20H2 <# if ((Get-RegCurrentVersion).CurrentBuild -lt 20000) { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Replacing Boot Media font wgl4_boot.ttf" Write-Host -ForegroundColor Yellow "Replacing this file resolves an issue where WinPE does not boot to the proper display resolution" $SourceUrl = 'https://github.com/OSDeploy/Archive-OSDCloud/raw/main/Media/boot/fonts/wgl4_boot.ttf' if (Test-WebConnection -Uri $SourceUrl) { Write-Host -ForegroundColor DarkGray "Source: $SourceUrl" Write-Host -ForegroundColor DarkGray "Destination: $DestinationMedia\boot\fonts\wgl4_boot.ttf" Save-WebFile -SourceUrl $SourceUrl -DestinationDirectory "$DestinationMedia\boot\fonts" -Overwrite | Out-Null } $SourceUrl = 'https://github.com/OSDeploy/Archive-OSDCloud/raw/main/Media/efi/microsoft/boot/fonts/wgl4_boot.ttf' if (Test-WebConnection -Uri $SourceUrl) { Write-Host -ForegroundColor DarkGray "Source: $SourceUrl" Write-Host -ForegroundColor DarkGray "Destination: $DestinationMedia\efi\microsoft\boot\fonts\wgl4_boot.ttf" Save-WebFile -SourceUrl $SourceUrl -DestinationDirectory "$DestinationMedia\efi\microsoft\boot\fonts" -Overwrite | Out-Null } } #> #endregion #region Mount-MyWindowsImage Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Mount Boot.wim" Write-Host -ForegroundColor Yellow "OSD Function: Mount-MyWindowsImage" $MountMyWindowsImage = Mount-MyWindowsImage $BootWim $MountPath = $MountMyWindowsImage.Path Write-Host -ForegroundColor DarkGray "MountPath: $MountPath" #endregion #region WinPE Information Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] WinPE Information" $WinPECurrentInfo = Get-RegCurrentVersion -Path $MountPath $WinPECurrentInfo #endregion #region WinRE #================================================= if ($PSBoundParameters.ContainsKey('WinRE')) { #================================================= # Wallpaper #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] WinRE Wallpaper" Write-Host -ForegroundColor Yellow "WinRE does not use the standard winpe.jpg and uses an all black winre.jpg" Write-Host -ForegroundColor Yellow "This step adds the default WinPE Wallpaper and modifies the Registry to point to winpe.jpg" $Wallpaper = '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAAgACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD5vooor+sD+KwooooAKKKKACiiigD/2Q==' [byte[]]$Bytes = [convert]::FromBase64String($Wallpaper) [System.IO.File]::WriteAllBytes("$env:TEMP\winpe.jpg",$Bytes) #[System.IO.File]::WriteAllBytes("$env:TEMP\winre.jpg",$Bytes) Write-Host -ForegroundColor DarkGray "Injecting $MountPath\Windows\System32\winpe.jpg" $null = robocopy "$env:TEMP" "$MountPath\Windows\System32" winpe.jpg /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log #Write-Host -ForegroundColor DarkGray "Injecting $MountPath\Windows\System32\winre.jpg" #$null = robocopy "$env:TEMP" "$MountPath\Windows\System32" winre.jpg /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log #================================================= # Build Driver #================================================= $InfFile = "$env:Temp\Set-WinREWallpaper.inf" New-Item -Path $InfFile -Force Set-Content -Path $InfFile -Value $WinREDriver -Encoding Unicode -Force #================================================= # Add Driver #================================================= Add-WindowsDriver -Path $MountPath -Driver $InfFile -ForceUnsigned #================================================= # Wireless #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] WinRE Wireless" Write-Host -ForegroundColor Yellow "These files need to be added to support Wireless" $SourceFile = "$env:SystemRoot\System32\dmcmnutils.dll" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray $SourceFile $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" dmcmnutils.dll /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log } else { Write-Warning "Could not find $SourceFile" } $SourceFile = "$env:SystemRoot\System32\mdmpostprocessevaluator.dll" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray $SourceFile $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" mdmpostprocessevaluator.dll /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log } else { Write-Warning "Could not find $SourceFile" } $SourceFile = "$env:SystemRoot\System32\mdmregistration.dll" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray $SourceFile $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" mdmregistration.dll /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log } else { Write-Warning "Could not find $SourceFile" } Write-Host -ForegroundColor DarkGray "Downloading https://github.com/okieselbach/Helpers/raw/master/WirelessConnect/WirelessConnect/bin/Release/WirelessConnect.exe" Save-WebFile -SourceUrl 'https://github.com/okieselbach/Helpers/raw/master/WirelessConnect/WirelessConnect/bin/Release/WirelessConnect.exe' -DestinationDirectory "$MountPath\Windows" | Out-Null } #endregion #region Set ADK Packages $ErrorActionPreference = 'Ignore' $WinPEOCs = $WindowsAdkPaths.WinPEOCs $OCPackages = @( 'WMI' 'HTA' 'NetFx' 'Scripting' 'PowerShell' 'SecureStartup' 'DismCmdlets' 'Dot3Svc' 'EnhancedStorage' 'FMAPI' 'GamingPeripherals' 'HSP-Driver' 'PPPoE' 'PlatformId' 'PmemCmdlets' 'RNDIS' 'SecureBootCmdlets' 'StorageWMI' 'WDS-Tools' ) if ($OSArch -eq 'ARM64') { # Require specific order of install of packages or it fails. #$OCPackages =@('Dot3Svc','EnhancedStorage','MDAC','NetFx','PowerShell','Scripting','SecureBootCmdlets','WMI','StorageWMI','PmemCmdlets','DismCmdlets','SecureStartup','x64-Support','PlatformId') $OCPackages += 'x64-Support' $OCPackages += 'MDAC' } <# if ($PSBoundParameters.ContainsKey('ARM64')) { # Require specific order of install of packages or it fails. #$OCPackages =@('Dot3Svc','EnhancedStorage','MDAC','NetFx','PowerShell','Scripting','SecureBootCmdlets','WMI','StorageWMI','PmemCmdlets','DismCmdlets','SecureStartup','x64-Support','PlatformId') $OCPackages += 'x64-Support' $OCPackages += 'MDAC' } #> #endregion #region Install Default en-us Language Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Adding default en-US ADK Packages" Write-Host -ForegroundColor Yellow "Dism Function: Add-WindowsPackage" $Lang = 'en-us' foreach ($Package in $OCPackages) { $SourceFile = "$WinPEOCs\WinPE-$Package.cab" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray "$SourceFile" $PackageName = "Add-WindowsPackage-WinPE-$Package" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-$PackageName.log" Try {Add-WindowsPackage -Path $MountPath -PackagePath $SourceFile -LogPath "$CurrentLog" | Out-Null} Catch {Write-Host -ForegroundColor Red $CurrentLog} } else { Write-Warning "Could not find $SourceFile" } } $SourceFile = "$WinPEOCs\$Lang\lp.cab" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray "$SourceFile" $PackageName = "Add-WindowsPackage-WinPE-lp_$Lang" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-$PackageName.log" Try {Add-WindowsPackage -Path $MountPath -PackagePath $SourceFile -LogPath "$CurrentLog" | Out-Null} Catch {Write-Host -ForegroundColor Red $CurrentLog} } else { Write-Warning "Could not find $SourceFile" } foreach ($Package in $OCPackages) { $SourceFile = "$WinPEOCs\$Lang\WinPE-$Package`_$Lang.cab" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray "$SourceFile" $PackageName = "Add-WindowsPackage-WinPE-$Package`_$Lang" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-$PackageName.log" Try {Add-WindowsPackage -Path $MountPath -PackagePath $SourceFile -LogPath "$CurrentLog" | Out-Null} Catch {Write-Host -ForegroundColor Red $CurrentLog} } } #endregion #region Save-WindowsImage Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Save Windows Image" Write-Host -ForegroundColor Yellow "Dism Function: Save-WindowsImage" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Save-WindowsImage.log" Save-WindowsImage -Path $MountPath -LogPath $CurrentLog | Out-Null #endregion #region Install Selected Language if ($Language -contains '*') { $Language = Get-ChildItem $WinPEOCs -Directory | Where-Object {$_.Name -ne 'en-us'} | Select-Object -ExpandProperty Name } foreach ($Lang in $Language) { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Adding $Lang ADK Packages" Write-Host -ForegroundColor Yellow "Dism Function: Add-WindowsPackage" $SourceFile = "$WinPEOCs\$Lang\lp.cab" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray "$SourceFile" $PackageName = "Add-WindowsPackage-WinPE-lp_$Lang" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-$PackageName.log" Try {Add-WindowsPackage -Path $MountPath -PackagePath $SourceFile -LogPath "$CurrentLog" | Out-Null} Catch {Write-Host -ForegroundColor Red $CurrentLog} } else { Write-Warning "Could not find $SourceFile" } foreach ($Package in $OCPackages) { $SourceFile = "$WinPEOCs\$Lang\WinPE-$Package`_$Lang.cab" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray "$SourceFile" $PackageName = "Add-WindowsPackage-WinPE-$Package`_$Lang" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-$PackageName.log" Try {Add-WindowsPackage -Path $MountPath -PackagePath $SourceFile -LogPath "$CurrentLog" | Out-Null} Catch {Write-Host -ForegroundColor Red $CurrentLog} } } # Generates a new Lang.ini file which is used to define the language packs inside the image if ( (Test-Path -Path "$MountPath\sources\lang.ini") ) { Write-Host -ForegroundColor DarkGray "Updating lang.ini" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Gen-LangINI.log" DISM /image:"$MountPath" /Gen-LangINI /distribution:"$MountPath" /LogPath:"$CurrentLog" } Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Save Windows Image" Write-Host -ForegroundColor Yellow "Dism Function: Save-WindowsImage" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Save-WindowsImage.log" Save-WindowsImage -Path $MountPath -LogPath $CurrentLog | Out-Null } #endregion #region International Settings if ($SetAllIntl -or $SetInputLocale) { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Current Get-Intl Settings" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Get-Intl.log" Dism /image:"$MountPath" /Get-Intl /LogPath:"$CurrentLog" } if ($SetAllIntl) { Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Applying Set-AllIntl" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Set-AllIntl.log" Dism /image:"$MountPath" /Set-AllIntl:$SetAllIntl /LogPath:"$CurrentLog" } if ($SetInputLocale) { Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Applying Set-InputLocale" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Set-InputLocale.log" Dism /image:"$MountPath" /Set-InputLocale:$SetInputLocale /LogPath:"$CurrentLog" } if ($SetAllIntl -or $SetInputLocale) { Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Updated Get-Intl Settings" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Get-Intl.log" Dism /image:"$MountPath" /Get-Intl /LogPath:"$CurrentLog" } #endregion #region Inject Additional Files Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] WinPE Additional Files" #================================================= # curl.exe #================================================= Write-Host -ForegroundColor Yellow "cURL is required for downloading files in WinPE" $SourceFile = "$env:SystemRoot\System32\curl.exe" Write-Host -ForegroundColor DarkGray $SourceFile if (Test-Path $SourceFile) { $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" curl.exe /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log } else { Write-Warning "Could not find $SourceFile" } #================================================= # setx.exe #================================================= Write-Host -ForegroundColor Yellow "Setx is required for setting System Variables" $SourceFile = "$env:SystemRoot\System32\setx.exe" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray $SourceFile $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" setx.exe /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log } else { Write-Warning "Could not find $SourceFile" } #================================================= # msinfo32.exe #================================================= Write-Host -ForegroundColor Yellow "MSInfo32 is helpful for verifying Hardware in WinPE" $SourceFile = "$env:SystemRoot\System32\msinfo32.exe" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray $SourceFile Write-Host -ForegroundColor DarkGray "$env:SystemRoot\System32\*\msinfo32.exe.mui" $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" msinfo32.exe /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" msinfo32.exe.mui /s /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log } else { Write-Warning "Could not find $SourceFile" } #================================================= # osk.exe #================================================= Write-Host -ForegroundColor Yellow "OSK adds WinPE On Screen Keyboard" $SourceFile = "$env:SystemRoot\System32\osk.exe" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray $SourceFile $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" osk.exe /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" osksupport.dll /s /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log } else { Write-Warning "Could not find $SourceFile" } $SourceFile = "$env:SystemRoot\System32\osksupport.dll" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray $SourceFile $null = robocopy "$env:SystemRoot\System32" "$MountPath\Windows\System32" osksupport.dll /b /ndl /np /r:0 /w:0 /xj /LOG+:$TemplateLogs\Robocopy.log } else { Write-Warning "Could not find $SourceFile" } #================================================= # 7zip x64 Portable #================================================= if ($PSBoundParameters.ContainsKey('Add7Zip')) { Add-7Zip2BootImage } #================================================= # Adding Microsoft DartConfig from MDT #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Microsoft DaRT Config from MDT" $SourceFile = "$env:ProgramFiles\Microsoft Deployment Toolkit\Templates\DartConfig8.dat" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray $SourceFile Copy-Item -Path $SourceFile -Destination "$MountPath\Windows\System32\DartConfig.dat" -Force } else { Write-Warning "Could not find $SourceFile" } #================================================= # Adding Microsoft DaRT #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Microsoft DaRT" $SourceFile = "$env:ProgramFiles\Microsoft DaRT\v10\Toolsx64.cab" if ($Name -match 'public') { Write-Host -ForegroundColor DarkGray 'Skipping Microsoft DaRT for Public Template' } elseif (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray $SourceFile expand.exe "$SourceFile" -F:*.* "$MountPath" | Out-Null if (!(Test-Path "$MountPath\Windows\System32\DartConfig.dat")) { Write-Warning "Microsoft DaRT requires MDT to be installed so DartConfig.dat can be copied" } } else { Write-Warning "Could not find $SourceFile" } #endregion #region Save-WindowsImage Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Save Windows Image" Write-Host -ForegroundColor Yellow "Dism Function: Save-WindowsImage" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Save-WindowsImage.log" Save-WindowsImage -Path $MountPath -LogPath $CurrentLog | Out-Null #endregion #region Set PowerShell Execution Policy Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Set WinPE PowerShell ExecutionPolicy to Bypass" Write-Host -ForegroundColor Yellow "OSD Function: Set-WindowsImageExecutionPolicy" Set-WindowsImageExecutionPolicy -Path $MountPath -ExecutionPolicy Bypass | Out-Null #endregion #region Enable PowerShell Gallery Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Enable WinPE PowerShell Gallery" Write-Host -ForegroundColor Yellow "OSD Function: Enable-PEWindowsImagePSGallery" Enable-PEWindowsImagePSGallery -Path $MountPath | Out-Null #endregion #region Remove winpeshl.ini $SourceFile = "$MountPath\Windows\System32\winpeshl.ini" if (Test-Path $SourceFile) { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Removing WinRE $SourceFile" Write-Host -ForegroundColor Yellow "This file is present when using WinRE.wim and needs to be removed for WinPE compatibility" Remove-Item -Path $SourceFile -Force } #endregion #region Registry Fixes Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Modifying WinPE CMD and PowerShell Console settings" Write-Host -ForegroundColor Yellow "This increases the buffer and sets the window metrics and default fonts" $RegistryConsole | Out-File -FilePath "$env:TEMP\RegistryConsole.reg" -Encoding ascii -Width 2000 -Force #Mount Registry Invoke-Exe reg load HKLM\Default "$MountPath\Windows\System32\Config\DEFAULT" Invoke-Exe reg import "$env:TEMP\RegistryConsole.reg" #reg add "HKLM\Default\Control Panel\Colors" /t REG_SZ /v Background /d "0 99 177" /f #Scaling <# reg add "HKLM\Default\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" /t REG_SZ /v "X:\Windows\System32\WirelessConnect.exe" /d "~ HIGHDPIAWARE" /f reg add "HKLM\Default\Control Panel\Desktop" /t REG_DWORD /v LogPixels /d 96 /f reg add "HKLM\Default\Control Panel\Desktop" /v Win8DpiScaling /t REG_DWORD /d 0x00000001 /f reg add "HKLM\Default\Control Panel\Desktop" /v DpiScalingVer /t REG_DWORD /d 0x00001018 /f #> #Unload Registry Invoke-Exe reg unload HKLM\Default | Out-Null #endregion #region CumulativeUpdate if ($CumulativeUpdate) { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Apply Cumulative Update" $Params = @{ ErrorAction = 'Stop' LogLevel = 'WarningsInfo' LogPath = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-CumulativeUpdate.log" PackagePath = $CumulativeUpdate Path = $MountPath Verbose = $true } Write-Host -ForegroundColor Yellow "Add-WindowsPackage -PackagePath" $Params.PackagePath Write-Host -ForegroundColor DarkGray "-Path" $Params.Path Write-Host -ForegroundColor DarkGray "-LogPath" $Params.LogPath try { Add-WindowsPackage @Params } catch { Write-Warning $PSItem.Exception.Message } finally { $Error.Clear() } Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Updated WinPE Information" $WinPEUpdatedInfo = Get-RegCurrentVersion -Path $MountPath $WinPEUpdatedInfo if ($WinPECurrentInfo.UBR -eq $WinPEUpdatedInfo.UBR) { Write-Warning 'There were no changes to the UBR after applying the Cumulative Update' Write-Warning 'You should verify that the Cumulative Update is for this version of WinPE' Write-Warning 'Boot Files will not be updated' Write-Host Start-Sleep -Seconds 15 } else { Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Update Boot Files" #bootmgr.efi Write-Host -ForegroundColor DarkGray "Source: $MountPath\Windows\boot\efi\bootmgr.efi" Write-Host -ForegroundColor Yellow "Destination: $DestinationMedia\bootmgr.efi" Copy-Item -Path "$MountPath\Windows\boot\efi\bootmgr.efi" -Destination "$DestinationMedia\bootmgr.efi" -Force #bootmgfw.efi Write-Host -ForegroundColor DarkGray "Source: $MountPath\Windows\boot\efi\bootmgfw.efi" Write-Host -ForegroundColor Yellow "Destination: $DestinationMedia\EFI\Boot\bootx64.efi" Copy-Item -Path "$MountPath\Windows\boot\efi\bootmgfw.efi" -Destination "$DestinationMedia\EFI\Boot\bootx64.efi" -Force #bootmgfw.efi Microsoft Guidance: https://learn.microsoft.com/en-us/windows/deployment/update/media-dynamic-update#update-winpe #Write-Host -ForegroundColor DarkGray "Source: $MountPath\Windows\boot\efi\bootmgfw.efi" #Write-Host -ForegroundColor Yellow "Destination: $DestinationMedia\bootmgfw.efi" #Copy-Item -Path "$MountPath\Windows\boot\efi\bootmgfw.efi" -Destination "$DestinationMedia\bootmgfw.efi" -Force } Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Dism Component Cleanup" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-DismComponentCleanup.log" #$CommandLine = "Dism /Cleanup-Image /StartComponentCleanup /Image:`"$MountPath`" /LogPath:`"$CurrentLog`"" Write-Host -ForegroundColor Yellow "Dism /Image:`"$MountPath`"" Write-Host -ForegroundColor DarkGray "/Cleanup-Image /StartComponentCleanup" Write-Host -ForegroundColor DarkGray "/LogPath:`"$CurrentLog`"" DISM /Image:"$MountPath" /Cleanup-Image /StartComponentCleanup /LogPath:"$CurrentLog" } #endregion #region Windows Packages Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Windows Packages installed in WinPE" Get-WindowsPackage -Path $MountPath | Sort-Object -Property PackageName | Format-Table -AutoSize #endregion #region Dismount Windows Image and Save Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Dismounting and Saving Windows Image" Write-Host -ForegroundColor Yellow "OSD Function: Dismount-MyWindowsImage" $MountMyWindowsImage | Dismount-MyWindowsImage -Save #endregion #region Export WIM to reduce the size Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Exporting Boot.wim" if ($PSBoundParameters.ContainsKey('WinRE')) { $BootWim = Join-Path $DestinationSources 'boot.wim' $WinREWim = Join-Path $DestinationSources 'winre.wim' if (Test-Path $BootWim) { Remove-Item -Path $BootWim -Force -ErrorAction Stop | Out-Null } Write-Host -ForegroundColor Yellow "Dism Function: Export-WindowsImage" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Export-WindowsImage.log" Export-WindowsImage -SourceImagePath $WinREWim -SourceIndex 1 -DestinationImagePath $BootWim -DestinationName 'Microsoft Windows PE (x64)' -LogPath $CurrentLog | Out-Null Remove-Item -Path $WinREWim -Force -ErrorAction Stop | Out-Null } else { $SourceImage = Join-Path $DestinationSources 'boot.wim' $DestinationImage = Join-Path $DestinationSources 'export.wim' if (Test-Path $DestinationImage) { Remove-Item -Path $DestinationImage -Force -ErrorAction Stop | Out-Null } Write-Host -ForegroundColor Yellow "Dism Function: Export-WindowsImage" $CurrentLog = "$TemplateLogs\$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))-Export-WindowsImage.log" Export-WindowsImage -SourceImagePath $SourceImage -SourceIndex 1 -DestinationImagePath $DestinationImage -LogPath $CurrentLog | Out-Null Remove-Item -Path $SourceImage -Force -ErrorAction Stop | Out-Null Rename-Item -Path $DestinationImage -NewName 'boot.wim' -Force -ErrorAction Stop | Out-Null } #endregion #region Create Config Directories Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Create Config Directories" $CreateDirectories = @( 'Config\AutopilotJSON', 'Config\AutopilotOOBE', 'Config\OOBEDeploy', 'Config\Scripts\SetupComplete', 'Config\Scripts\Shutdown', 'Config\Scripts\Startup', 'Config\Scripts\StartNet' ) if ($Name -match 'public') { Write-Host -ForegroundColor DarkGray 'Skipping Config Directories for Public Template' } else { foreach ($Item in $CreateDirectories) { $SourcePath = "$OSDCloudTemplate\$Item" if (-NOT (Test-Path $SourcePath)) { Write-Host -ForegroundColor DarkGray $SourcePath New-Item -Path $SourcePath -ItemType Directory -Force | Out-Null } } } #endregion #region Create OSDCloud ISOs Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Create OSDCloud Template ISOs" $isoFileName = 'OSDCloud.iso' $isoLabel = 'OSDCloud' $null = New-WindowsAdkISO -MediaPath "$OSDCloudTemplate\Media" -isoFileName $isoFileName -isoLabel $isoLabel #endregion #region Set OSDCloud Template Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "[$(Get-Date -format s)] Set-OSDCloudTemplate -Name $Name" $WinPE = [PSCustomObject]@{ BuildDate = (Get-Date).ToString('yyyy.MM.dd.HHmmss') Version = [Version](Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).Version } $WinPE | ConvertTo-Json | Out-File "$OSDCloudTemplate\Logs\winpe.json" -Encoding ascii -Width 2000 -Force Set-OSDCloudTemplate -Name $Name #endregion #region Complete $TemplateEndTime = Get-Date $TemplateTimeSpan = New-TimeSpan -Start $TemplateStartTime -End $TemplateEndTime Write-Host -ForegroundColor DarkGray "================================================" Write-Host -ForegroundColor Yellow "[$(Get-Date -format s)] $($MyInvocation.MyCommand.Name) " -NoNewline Write-Host -ForegroundColor Cyan "Completed in $($TemplateTimeSpan.ToString("mm' minutes 'ss' seconds'"))" Write-Host -ForegroundColor Cyan "OSDCloud Template created at $OSDCloudTemplate" Write-Host -ForegroundColor DarkGray "================================================" Stop-Transcript #endregion } function Set-OSDCloudTemplate { <# .SYNOPSIS Sets configuration values by using Set-OSDCloudTemplate. .DESCRIPTION Provides the implementation for Set-OSDCloudTemplate. .PARAMETER Name Specifies the value for Name. .EXAMPLE Set-OSDCloudTemplate -Name <Name> Runs Set-OSDCloudTemplate with common parameters. .NOTES Author: David Segura - Recast Software 2026-07-09 - Updated comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param ( [Parameter(Position=0)] [System.String] #Name of the OSDCloud Template $Name = 'default' ) #region Block $CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent() $CurrentPrincipal = [Security.Principal.WindowsPrincipal]::new($CurrentIdentity) if (-not $CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrative rights are required" return } Block-PowerShellVersionLt5 Block-WinPE #endregion #region Set Template Path if ($Name -ne 'default') { # OSDeploy Compatibility if (Test-Path "$env:ProgramData\OSDeploy\MyBootMedia\$Name") { $OSDCloudTemplate = "$env:ProgramData\OSDeploy\MyBootMedia\$Name" } elseif (Test-Path "$env:ProgramData\OSDK\BootMedia\$Name") { $OSDCloudTemplate = "$env:ProgramData\OSDK\BootMedia\$Name" } elseif (Test-Path "C:\OSDKIT\BootMedia\$Name") { $OSDCloudTemplate = "C:\OSDKIT\BootMedia\$Name" } else { $OSDCloudTemplate = "$env:ProgramData\OSDCloud\Templates\$Name" } if (-NOT (Test-Path "$OSDCloudTemplate\Media\sources\boot.wim")) { $Name = 'default' } } if ($Name -eq 'default') { $OSDCloudTemplate = "$env:ProgramData\OSDCloud" if (Test-Path "$env:ProgramData\OSDCloud\template.json") { $null = Remove-Item -Path "$env:ProgramData\OSDCloud\template.json" -Force } } else { $TemplateSettings = [PSCustomObject]@{ TemplatePath = $OSDCloudTemplate } $TemplateSettings | ConvertTo-Json | Out-File "$env:ProgramData\OSDCloud\template.json" -Encoding ascii -Width 2000 -Force } $OSDCloudTemplate #endregion #region DEV <# if ((Test-Path "$env:ProgramData\OSDCloud\Config") -or (Test-Path "$env:ProgramData\OSDCloud\Logs") -or (Test-Path "$env:ProgramData\OSDCloud\Media")) { Write-Warning "[$(Get-Date -format s)] Migrating existing OSDCloud Template to $OSDCloudTemplate" $null = robocopy "$env:ProgramData\OSDCloud\Config" "$OSDCloudTemplate\Config" *.* /move /e /np /njh /njs /r:0 /w:0 $null = robocopy "$env:ProgramData\OSDCloud\Autopilot\Profiles" "$OSDCloudTemplate\Config\AutopilotJSON" *.* /move /e /np /njh /njs /r:0 /w:0 $null = robocopy "$env:ProgramData\OSDCloud\Logs" "$OSDCloudTemplate\Logs" *.* /move /e /np /njh /njs /r:0 /w:0 $null = robocopy "$env:ProgramData\OSDCloud\Media" "$OSDCloudTemplate\Media" *.* /move /e /np /njh /njs /r:0 /w:0 $null = robocopy "$env:ProgramData\OSDCloud" "$OSDCloudTemplate" winpe.json /move /np /njh /njs /r:0 /w:0 $null = robocopy "$env:ProgramData\OSDCloud" "$OSDCloudTemplate" *.iso /move /np /njh /njs /r:0 /w:0 } #> #endregion } Register-ArgumentCompleter -CommandName Set-OSDCloudTemplate -ParameterName 'Name' -ScriptBlock {Get-OSDCloudTemplateNames | ForEach-Object {if ($_.Contains(' ')) {"'$_'"} else {$_}}} #https://github.com/PowerShell/PowerShell/issues/11330 #https://github.com/SteveL-MSFT/NetConfiguration/blob/master/NetConfiguration/NetConfiguration.psm1#L29-L45 |