Public/OSDCloudIPU/New-OSDCloudOSWimFile.ps1
function New-OSDCloudOSWimFile { <# Log Files for IPU: https://learn.microsoft.com/en-us/windows/deployment/upgrade/log-files Setup Command Line: https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-setup-command-line-options?view=windows-11 #> [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(ParameterSetName = 'Default')] [ValidateSet( 'Windows 11 23H2 x64', 'Windows 11 23H2 ARM64', 'Windows 11 22H2 x64', 'Windows 11 21H2 x64', 'Windows 10 22H2 x64', 'Windows 10 22H2 ARM64')] [System.String] $OSName = 'Windows 11 23H2 x64', #Operating System Edition of the Windows installation #Alias = Edition [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Legacy')] [ValidateSet('Home','Home N','Home Single Language','Education','Education N','Enterprise','Enterprise N','Pro','Pro N')] [Alias('Edition')] [System.String] $OSEdition = 'Pro', #Operating System Language of the Windows installation #Alias = Culture, OSCulture [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Legacy')] [ValidateSet ( 'ar-sa','bg-bg','cs-cz','da-dk','de-de','el-gr', 'en-gb','en-us','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' )] [Alias('Culture','OSCulture')] [System.String] $OSLanguage = 'en-us', #License of the Windows Operating System [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'Legacy')] [ValidateSet('Retail','Volume')] [Alias('License','OSLicense','Activation')] [System.String] $OSActivation = 'Retail', #Create ISO File - Requries Windows ADK (oscdimg.exe) [Parameter(ParameterSetName = 'Default')] [Switch] $CreateISO ) #region Admin Elevation $whoiam = [system.security.principal.windowsidentity]::getcurrent().name $isElevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") if ($isElevated) { Write-Host -ForegroundColor Green "[+] Running as $whoiam and IS Admin Elevated" } else { Write-Warning "[-] Running as $whoiam and is NOT Admin Elevated" Break } #================================================ # Get Index & OS Info #================================================ <# if ($OSName -match "ARM64"){ $OSArch = 'ARM64' $IndexInfo = Get-OSDCloudOperatingSystemsIndexes -OSArch ARM64 | Where-Object {$_.Name -match $OSName} | Where-Object {$_.Activation -eq $OSActivation} | Where-Object {$_.Language -eq $OSLanguage} } else { $OSArch = 'x64' $IndexInfo = Get-OSDCloudOperatingSystemsIndexes | Where-Object {$_.Name -match $OSName} | Where-Object {$_.Activation -eq $OSActivation} | Where-Object {$_.Language -eq $OSLanguage} } #> if ($OSName -match "ARM64"){ $OSArch = 'ARM64' $OSDCloudOperatingSystem = (Get-OSDCloudOperatingSystemsIndexes -OSArch ARM64) | Where-Object {$_.Name -match $OSName} | Where-Object {$_.Activation -eq $OSActivation} | Where-Object {$_.Language -eq $OSLanguage} } else { $OSArch = 'x64' $OSDCloudOperatingSystem = Get-OSDCloudOperatingSystemsIndexes | Where-Object {$_.Name -match $OSName} | Where-Object {$_.Activation -eq $OSActivation} | Where-Object {$_.Language -eq $OSLanguage} } $OSEditionID = "$($OSDCloudOperatingSystem.Version) $OSEdition" $OSImageIndex = $OSDCloudOperatingSystem.Indexes.$OSEditionID if ($OSImageIndex -eq $null){ Write-Host -ForegroundColor Red "Unable to determine OSImageIndex for Index $OSEdition" Write-Host -ForegroundColor Yellow "Available Indexes are $($OSDCloudOperatingSystem.IndexNames.replace($(($OSDCloudOperatingSystem).Version),'') -join ', ')" throw "Unable to determine OSImageIndex for $OSName $OSEdition $OSActivation $OSLanguage" } #$OSBuild = $OSDCloudOperatingSystem.Build #$OSReleaseID = $OSDCloudOperatingSystem.ReleaseID #$OSVersion = $OSDCloudOperatingSystem.Version #$ImageFileName = $OSDCloudOperatingSystem.FileName #$ImageFileUrl = $OSDCloudOperatingSystem.Url $ImageFileItem = Find-OSDCloudFile -Name $OSDCloudOperatingSystem.FileName -Path '\OSDCloud\OS\' | Sort-Object FullName | Where-Object {$_.Length -gt 3GB} $ImageFileItem = $ImageFileItem | Where-Object {$_.FullName -notlike "C*"} | Where-Object {$_.FullName -notlike "X*"} | Select-Object -First 1 Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor DarkCyan "These are set based on your input parameters" Write-Host -ForegroundColor Cyan "OSEditionId: " -NoNewline Write-Host -ForegroundColor Green $OSEdition Write-Host -ForegroundColor Cyan "OSImageIndex: " -NoNewline Write-Host -ForegroundColor Green $OSImageIndex Write-Host -ForegroundColor Cyan "OSLanguage: " -NoNewline Write-Host -ForegroundColor Green $OSLanguage Write-Host -ForegroundColor Cyan "OSActivation: " -NoNewline Write-Host -ForegroundColor Green $OSActivation Write-Host -ForegroundColor Cyan "OSArch: " -NoNewline Write-Host -ForegroundColor Green $OSArch Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Starting Feature Update lookup and Download" #============================================================================ #region Detect & Download ESD File #============================================================================ $ScratchLocation = 'c:\OSDCloud\IPU' $OSMediaLocation = 'c:\OSDCloud\OS' $MediaLocation = "$ScratchLocation\Media\$OSName" if (!(Test-Path -Path $OSMediaLocation)){New-Item -Path $OSMediaLocation -ItemType Directory -Force | Out-Null} if (!(Test-Path -Path $ScratchLocation)){New-Item -Path $ScratchLocation -ItemType Directory -Force | Out-Null} if (Test-Path -Path $MediaLocation){Remove-Item -Path $MediaLocation -Force -Recurse} New-Item -Path $MediaLocation -ItemType Directory -Force | Out-Null $ESD = Get-FeatureUpdate -OSName $OSName -OSActivation $OSActivation -OSLanguage $OSLanguage -OSArchitecture $OSArch if (!($ESD)){ Write-Host -ForegroundColor Red "Unable to Determine proper ESD Upgrade File" throw "Unable to Determine proper ESD Upgrade File" } Write-Host -ForegroundColor Cyan "Name: " -NoNewline Write-Host -ForegroundColor Green $ESD.Name Write-Host -ForegroundColor Cyan "Architecture: " -NoNewline Write-Host -ForegroundColor Green $ESD.Architecture Write-Host -ForegroundColor Cyan "Activation: " -NoNewline Write-Host -ForegroundColor Green $ESD.Activation Write-Host -ForegroundColor Cyan "Build: " -NoNewline Write-Host -ForegroundColor Green $ESD.Build Write-Host -ForegroundColor Cyan "FileName: " -NoNewline Write-Host -ForegroundColor Green $ESD.FileName Write-Host -ForegroundColor Cyan "Url: " -NoNewline Write-Host -ForegroundColor Green $ESD.Url Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Getting Content for Upgrade Media" <##> #Build Media Paths $SubFolderName = "$($ESD.Version) $($ESD.ReleaseId)" $ImageFolderPath = "$OSMediaLocation\$SubFolderName" if (!(Test-Path -Path $ImageFolderPath)){New-Item -Path $ImageFolderPath -ItemType Directory -Force | Out-Null} $ImagePath = "$ImageFolderPath\$($ESD.FileName)" $ImageDownloadRequired = $true #Check Flash Drive for Media $OSDCloudUSB = Get-Volume.usb | Where-Object {($_.FileSystemLabel -match 'OSDCloud') -or ($_.FileSystemLabel -match 'BHIMAGE')} | Select-Object -First 1 if ($OSDCloudUSB){ $USBImagePath = "$($OSDCloudUSB.DriveLetter):\OSDCloud\OS\$SubFolderName\$($ESD.FileName)" if ((Test-Path -path $USBImagePath) -and (!(Test-Path -path $ImagePath))){ Write-Host -ForegroundColor Green "Found media on OSDCloudUSB - Copying Local" Copy-Item -Path $USBImagePath -Destination $ImagePath } } #Test for Media if (Test-path -path $ImagePath){ Write-Host -ForegroundColor Gray "Found previously downloaded media: $ImagePath" write-host -ForegroundColor Gray " ... Getting SHA1 Hash for validation" $SHA1Hash = Get-FileHash $ImagePath -Algorithm SHA1 if ($SHA1Hash.Hash -eq $esd.SHA1){ Write-Host -ForegroundColor Gray "SHA1 Match $($SHA1Hash.Hash), skipping Download" $ImageDownloadRequired = $false } else { Write-Host -ForegroundColor Gray "SHA1 Match Failed on $ImagePath, removing content" } } if ($ImageDownloadRequired -eq $true){ #Save-WebFile -SourceUrl $ESD.Url -DestinationDirectory $ScratchLocation -DestinationName $ESD.FileName Write-Host -ForegroundColor Gray "Starting Download to $ImagePath, this takes awhile" <# This was taking way too long for some files #Get ESD Size $req = [System.Net.HttpWebRequest]::Create("$($ESD.Url)") $res = $req.GetResponse() (Invoke-WebRequest $ESD.Url -Method Head).Headers.'Content-Length' $ESDSizeMB = $([Math]::Round($res.ContentLength /1000000)) Write-Host "Total Size: $ESDSizeMB MB" #> #Clear Out any Previous Attempts $ExistingBitsJob = Get-BitsTransfer -Name "$($ESD.FileName)" -AllUsers -ErrorAction SilentlyContinue If ($ExistingBitsJob) { Remove-BitsTransfer -BitsJob $ExistingBitsJob } if ((Get-Service -name BITS).Status -ne "Running"){ Write-Host -ForegroundColor Yellow "BITS Service is not Running, which is required to download ESD File, attempting to Start" $StartBITS = Start-Service -Name BITS -PassThru Start-Sleep -Seconds 2 if ($StartBITS.Status -ne "Running"){ } } #Start Download using BITS Write-Host -ForegroundColor DarkGray "Start-BitsTransfer -Source $ESD.Url -Destination $ImageFolderPath -DisplayName $($ESD.FileName) -Description 'Windows Media Download' -RetryInterval 60" $BitsJob = Start-BitsTransfer -Source $ESD.Url -Destination $ImageFolderPath -DisplayName "$($ESD.FileName)" -Description "Windows Media Download" -RetryInterval 60 If ($BitsJob.JobState -eq "Error"){ write-Host "BITS tranfer failed: $($BitsJob.ErrorDescription)" } } #endregion Detect & Download ESD File #============================================================================ #region Extract of ESD file to create Setup Content #============================================================================ #https://www.deploymentresearch.com/how-to-really-create-a-windows-10-build-10041-iso-no-3rd-party-tools-needed/ #Using info from Johan's process to export properly #DISM commands are left for reference only. #Grab ESD File and create bootable ISO if ((!(Test-Path -Path $ImagePath)) -or (!(Test-Path -Path $MediaLocation))){ if (!(Test-Path -Path $ImagePath)){ Write-Host -ForegroundColor Red "Missing $ImagePath, double check download process" throw "Failed to find $ImagePath, double check download process" } if (!(Test-Path -Path $MediaLocation)){ Write-Host -ForegroundColor Red "Missing $MediaLocation, double check folder exist" throw "Failed to find $MediaLocation, double check folder exist" } } if ((Test-Path -Path $ImagePath) -and (Test-Path -Path $MediaLocation)){ Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Starting Extract of ESD file to create Setup Content" $ApplyPath = $MediaLocation Write-Host -ForegroundColor Gray "Expanding $ImagePath Index 1 to $ApplyPath" $Expand = Expand-WindowsImage -ImagePath $ImagePath -Index 1 -ApplyPath $ApplyPath # Create empty boot.wim file with compression type set to maximum $EmptyFolder = "$($env:TEMP)\EmptyFolder" New-Item -ItemType Directory -Path $EmptyFolder -Force | Out-Null #dism.exe /Capture-Image /ImageFile:$ISOMediaFolder\sources\boot.wim /CaptureDir:$EmptyFolder /Name:EmptyIndex /Compress:max New-WindowsImage -ImagePath $ApplyPath\Sources\boot.wim -CapturePath $EmptyFolder -Name EmptyIndex -Description "Empty Index" -CompressionType Fast # Export base Windows PE to empty boot.wim file (creating a second index) #dism.exe /Export-image /SourceImageFile:$ESDFile /SourceIndex:2 /DestinationImageFile:$ISOMediaFolder\sources\boot.wim /Compress:Recovery /Bootable Export-WindowsImage -SourceImagePath $ImagePath -SourceIndex 2 -DestinationImagePath "$ApplyPath\Sources\boot.wim" -CompressionType Fast -CheckIntegrity -Setbootable # Delete the first empty index in boot.wim #dism.exe /Delete-Image /ImageFile:$ISOMediaFolder\sources\boot.wim /Index:1 Remove-WindowsImage -ImagePath $ApplyPath\Sources\boot.wim -Index 1 # Export Windows PE with Setup to boot.wim file #dism.exe /Export-image /SourceImageFile:$ESDFile /SourceIndex:3 /DestinationImageFile:$ISOMediaFolder\sources\boot.wim /Compress:Recovery /Bootable Export-WindowsImage -SourceImagePath $ImagePath -SourceIndex 3 -DestinationImagePath "$ApplyPath\Sources\boot.wim" -CompressionType Fast -CheckIntegrity -Setbootable # Create empty install.wim file with MDT/ConfigMgr friendly compression type (maximum) #dism.exe /Capture-Image /ImageFile:$ISOMediaFolder\sources\install.wim /CaptureDir:C:\EmptyFolder /Name:EmptyIndex /Compress:max New-WindowsImage -ImagePath $ApplyPath\Sources\install.wim -CapturePath $EmptyFolder -Name EmptyIndex -Description "Empty Index" -CompressionType Fast #Export the OS Image to the install.wim file Write-Host -ForegroundColor Gray "Expanding $ImagePath Index $OSImageIndex to $ApplyPath\Sources\install.wim" #dism.exe /Export-image /SourceImageFile:$ESDFile /SourceIndex:4 /DestinationImageFile:$ISOMediaFolder\sources\install.wim /Compress:Recovery ##Export-WindowsImage -SourceImagePath $ImagePath -SourceIndex 5 -DestinationImagePath "$ApplyPath\Sources\install.wim" -CompressionType max -CheckIntegrity $Expand = Export-WindowsImage -SourceImagePath $ImagePath -SourceIndex $OSImageIndex -DestinationImagePath "$ApplyPath\Sources\install.wim" -CheckIntegrity -CompressionType Fast $null = $Expand # Delete the first empty index in install.wim #dism.exe /Delete-Image /ImageFile:$ISOMediaFolder\sources\install.wim /Index:1 Remove-WindowsImage -ImagePath $ApplyPath\Sources\install.wim -Index 1 } #endregion Extract of ESD file to create Setup Content if (!(Test-Path -Path "$MediaLocation\Setup.exe")){ Write-Host -ForegroundColor Red "Setup.exe not found, something went wrong" throw } if (!(Test-Path -Path "$MediaLocation\sources\install.wim")){ Write-Host -ForegroundColor Red "install.wim not found, something went wrong" throw } Write-Host -ForegroundColor DarkGray "=========================================================================" if ($CreateISO){ $PathToOscdimg = (Get-AdkPaths).oscdimgexe if (!(Test-Path -Path $PathToOscdimg)){ Write-Host -ForegroundColor Red "oscdimg.exe not found, unable to create ISO File" throw "oscdimg.exe not found, unable to create ISO File" } else { Write-Host -ForegroundColor Cyan "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Creating ISO File" $BootData='2#p0,e,b"{0}"#pEF,e,b"{1}"' -f "$ApplyPath\boot\etfsboot.com","$ApplyPath\efi\Microsoft\boot\efisys.bin" $ISOFile = "`"$ScratchLocation\$($ESD.Version) $($ESD.ReleaseId) $($ESD.Architecture).iso`"" $ISOFilePath = "$ScratchLocation\$($ESD.Version) $($ESD.ReleaseId) $($ESD.Architecture).iso" if (Test-Path -Path $ISOFilePath){Remove-Item -Path $ISOFilePath -Force} $ISOMedia = "`"$ApplyPath`"" $Proc = Start-Process -FilePath $PathToOscdimg -ArgumentList @("-bootdata:$BootData",'-u2','-udfver102',"$ISOMedia","$ISOFile") -PassThru -Wait -NoNewWindow if($Proc.ExitCode -ne 0) { Throw "Failed to generate ISO with exitcode: $($Proc.ExitCode)" } if (Test-Path -Path $ISOFilePath){ Write-Host -ForegroundColor Green "ISO File Created: $ISOFile" } else { Write-Host -ForegroundColor Red "Failed to Create ISO File" } } Write-Host -ForegroundColor DarkGray "=========================================================================" } } |