build/Build-OSDCloudOperatingSystemsv3.ps1
<#
Get ESD files from the Microsoft Creation Tool's list This file needs to be updated for each release of Windows This will create a table of all of the ESD files from MS, then create a "database" for OSDCloud Gary Blok's attempt to help David Changes 23.12.23 - Several Updates - Updated Win10 22H2 CAB URL for newer ESD files - Updated Win11 23H2 CAB URL for newer ESD files - Started making modifications to support Win 11 22H2 GA ESD Files (22621.382) #> # Import OSD Module Import-Module OSD -Force -ErrorAction Stop $StagingFolder = "$env:TEMP\OSDStaging" if (!(Test-Path -Path $StagingFolder)){ new-item -Path $StagingFolder -ItemType Directory | Out-Null } $WindowsTable = @( @{ Version = 'Win1022H2';LocalCab = "Win1022H2.Cab"; URL = "https://download.microsoft.com/download/7/9/c/79cbc22a-0eea-4a0d-89c0-054a1b3aa8e0/products.cab"} @{ Version = 'Win1121H2';LocalCab = "Win1121H2.Cab"; URL = "https://download.microsoft.com/download/1/b/4/1b4e06e2-767a-4c9a-9899-230fe94ba530/products_Win11_20211115.cab"} @{ Version = 'Win1122H2';LocalCab = "Win1122H2.Cab"; URL = "https://download.microsoft.com/download/b/1/9/b19bd7fd-78c4-4f88-8c40-3e52aee143c2/products_win11_20230510.cab.cab"} @{ Version = 'Win1123H2';LocalCab = "Win1123H2.Cab"; URL = "https://download.microsoft.com/download/6/2/b/62b47bc5-1b28-4bfa-9422-e7a098d326d4/products_win11_20231208.cab"} ) #region functions borrowed from HPCMSL function Invoke-HPPrivateExpandCAB { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $cab, [Parameter(Mandatory = $true)] $expectedFile ) Write-Verbose "Expanding CAB $cab to $cab.dir" $target = "$cab.dir" Invoke-HPPrivateSafeRemove -Path $target -Recurse Write-Verbose "Expanding $cab to $target" $result = New-Item -Force $target -ItemType Directory Write-Verbose "Created folder $result" $shell = New-Object -ComObject "Shell.Application" $exception = $null try { if (!$?) { $(throw "unable to create $comObject object") } $sourceCab = $shell.Namespace($cab).items() $DestinationFolder = $shell.Namespace($target) $DestinationFolder.CopyHere($sourceCab) } catch { $exception = $_.Exception } finally { [System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$shell) | Out-Null [System.GC]::Collect() [System.GC]::WaitForPendingFinalizers() } if ($exception) { throw "Failed to decompress $cab. $($exception.Message)." } $downloadedOk = Test-Path $expectedFile if ($downloadedOk -eq $false) { throw "Invalid cab file, did not find $expectedFile in contents" } return $expectedFile } function Invoke-HPPrivateSafeRemove { [CmdletBinding()] param( [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [string[]]$path, [Parameter(Mandatory = $false)] [switch]$recurse ) foreach ($p in $path) { if (Test-Path $p) { Write-Verbose "Removing $p" Remove-Item $p -Recurse:$recurse } } } #endregion #region functions function Test-WebConnection{ [CmdletBinding()] param ( [Parameter(ValueFromPipeline)] # Uri to test [System.Uri] $Uri = 'google.com' ) $Params = @{ Method = 'Head' Uri = $Uri UseBasicParsing = $true Headers = @{'Cache-Control'='no-cache'} } try { Write-Verbose "Test-WebConnection OK: $Uri" Invoke-WebRequest @Params | Out-Null $true } catch { Write-Verbose "Test-WebConnection FAIL: $Uri" $false } finally { $Error.Clear() } } #endregion $ESDInfo = @() ForEach ($Option in $WindowsTable){ Invoke-WebRequest -Uri $Option.URL -UseBasicParsing -OutFile "$StagingFolder\$($Option.LocalCab)" -ErrorAction SilentlyContinue -Verbose $file = Invoke-HPPrivateExpandCAB -cab "$StagingFolder\$($Option.LocalCab)" -expectedFile "$StagingFolder\$($Option.LocalCab).dir\products.xml" -Verbose [XML]$XML = Get-Content -Raw -Path "$StagingFolder\$($Option.LocalCab).dir\products.xml" $ESDInfo += $XML.MCT.Catalogs.Catalog.PublishedMedia.Files.File } #Remove Dups $UniqueESDInfo = $ESDInfo | Group-Object -Property FileName | %{$_.Group[0]} #Clean Up Results $x64ESDInfo = $UniqueESDInfo | Where-Object {$_.Architecture -eq "x64"} $x64ESDInfo = $x64ESDInfo | Where-Object {$_.Edition -eq "Professional" -or $_.Edition -eq "Education" -or $_.Edition -eq "Enterprise" -or $_.Edition -eq "Professional" -or $_.Edition -eq "HomePremium"} $ARM64ESDInfo = $UniqueESDInfo | Where-Object {$_.Architecture -eq "ARM64"} $ARM64ESDInfo = $ARM64ESDInfo | Where-Object {$_.Edition -eq "Professional" -or $_.Edition -eq "Education" -or $_.Edition -eq "Enterprise" -or $_.Edition -eq "Professional" -or $_.Edition -eq "HomePremium"} $ARM64ESDInfo = $ARM64ESDInfo | Where-Object {$_.FileName -match '19045' -or $_.FileName -match '22631'} Import-Module -Name OSD -Force #================================================= # FeatureUpdates x64 #================================================= $Results = $x64ESDInfo $Results = $Results | Select-Object ` @{Name='Status';Expression={($null)}}, ` @{Name='ReleaseDate';Expression={($null)}}, ` @{Name='Name';Expression={($_.Title)}}, ` @{Name='Version';Expression={($null)}}, ` @{Name='ReleaseID';Expression={($_.null)}}, ` @{Name='Architecture';Expression={($_.Architecture)}}, ` @{Name='Language';Expression={($_.LanguageCode)}}, ` @{Name='Activation';Expression={($null)}}, ` @{Name='Build';Expression={($null)}}, ` @{Name='FileName';Expression={($_.FileName)}}, ` @{Name='ImageIndex';Expression={($null)}}, ` @{Name='ImageName';Expression={($null)}}, ` @{Name='Url';Expression={($_.FilePath)}}, ` @{Name='SHA1';Expression={($_.Sha1)}}, ` @{Name='UpdateID';Expression={($_.UpdateID)}}, ` @{Name='Win10';Expression={($null)}}, ` @{Name='Win11';Expression={($null)}} foreach ($Result in $Results) { #================================================= # Language #================================================= #if ($Result.FileName -match 'sr-latn-rs') { # $Result.Language = 'sr-latn-rs' #} #else { # $Regex = "[a-zA-Z]+-[a-zA-Z]+" # $Result.Language = ($Result.FileName | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #} #================================================= # Activation #================================================= if ($Result.Url -match 'business') { $Result.Activation = 'Volume' } else { $Result.Activation = 'Retail' } #================================================= # Build #================================================= $Regex = "[0-9]*\.[0-9]+" $Result.Build = ($Result.FileName | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #================================================= # OS Version #================================================= if ($Result.Build -lt 22000) { $Result.Version = 'Windows 10' $Result.Win10 = $true $Result.Win11 = $false } if ($Result.Build -ge 22000) { $Result.Version = 'Windows 11' $Result.Win10 = $false $Result.Win11 = $true } #================================================= # ReleaseID #================================================= if ($Result.Build -match "19045"){$Result.ReleaseID = "22H2"} if ($Result.Build -match "22000"){$Result.ReleaseID = "21H2"} if ($Result.Build -match "22621"){$Result.ReleaseID = "22H2"} if ($Result.Build -match "22631"){$Result.ReleaseID = "23H2"} #$Result.ReleaseID = (($Result.FileName).Split(".")[3]).Split("_")[0] #worked on some, not others #================================================= # Date #================================================= $DateString = (($Result.FileName).Split(".")[2]).Split("-")[0] $Date = [datetime]::parseexact($DateString, 'yyMMdd', $null) $Result.ReleaseDate = (Get-Date $Date -Format "yyyy-MM-dd") #================================================= # SHA1 #================================================= #$Regex = "[0-9a-f]{40}" #$Result.SHA1 = ($Result.FileName | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #$Result.SHA1 = ((Split-Path -Leaf $Result.Url) | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #================================================= # Name #================================================= if ($Result.Activation -eq 'Volume') { $Result.Name = $Result.Version + ' ' + $Result.ReleaseID + ' x64 ' + $Result.Language + ' Volume ' + $Result.Build } else { $Result.Name = $Result.Version + ' ' + $Result.ReleaseID + ' x64 ' + $Result.Language + ' Retail ' + $Result.Build } #================================================= } $ResultsMCT = $Results | Sort-Object -Property Name #================================================= # LEGACY FeatureUpdates x64 #================================================= $WSUSResults = Get-WSUSXML -Catalog FeatureUpdate -Silent $WSUSResults = $WSUSResults | Where-Object {$_.UpdateArch -eq 'x64'} $WSUSResults = $WSUSResults | Select-Object ` @{Name='Status';Expression={($_.OSDStatus)}}, ` @{Name='ReleaseDate';Expression={(Get-Date $_.CreationDate -Format "yyyy-MM-dd")}}, ` @{Name='Name';Expression={($_.Title)}}, ` @{Name='Version';Expression={($_.UpdateOS)}}, ` @{Name='ReleaseID';Expression={($_.UpdateBuild)}}, ` @{Name='Architecture';Expression={($_.UpdateArch)}}, ` @{Name='Language';Expression={($null)}}, ` @{Name='Activation';Expression={($null)}}, ` @{Name='Build';Expression={($null)}}, ` @{Name='FileName';Expression={((Split-Path -Leaf $_.FileUri))}}, ` @{Name='ImageIndex';Expression={($null)}}, ` @{Name='ImageName';Expression={($null)}}, ` @{Name='Url';Expression={($_.FileUri)}}, ` @{Name='SHA1';Expression={($null)}}, ` @{Name='UpdateID';Expression={($_.UpdateID)}}, ` @{Name='Win10';Expression={($null)}}, ` @{Name='Win11';Expression={($null)}} foreach ($WSUSResult in $WSUSResults) { #================================================= # Language #================================================= if ($WSUSResult.FileName -match 'sr-latn-rs') { $WSUSResult.Language = 'sr-latn-rs' } else { $Regex = "[a-zA-Z]+-[a-zA-Z]+" $WSUSResult.Language = ($WSUSResult.FileName | Select-String -AllMatches -Pattern $Regex).Matches[0].Value } #================================================= # Activation #================================================= if ($WSUSResult.Url -match 'business') { $WSUSResult.Activation = 'Volume' } else { $WSUSResult.Activation = 'Retail' } #================================================= # Support Win11 22H2 GA ESD #================================================= if (($WSUSResult.Version -match 'Windows 11') -and ($WSUSResult.ReleaseID -eq '22H2')) { $WSUSResult.ReleaseID = '22H2-GA' } #================================================= # Version #================================================= if ($WSUSResult.Name -match 'Windows 10') { $WSUSResult.Version = 'Windows 10' $WSUSResult.Win10 = $true $WSUSResult.Win11 = $false } if ($WSUSResult.Name -match 'Windows 11') { $WSUSResult.Version = 'Windows 11' $WSUSResult.Win10 = $false $WSUSResult.Win11 = $true } #================================================= # Build #================================================= $Regex = "[0-9]*\.[0-9]+" $WSUSResult.Build = ($WSUSResult.FileName | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #================================================= # SHA1 #================================================= $Regex = "[0-9a-f]{40}" $WSUSResult.SHA1 = ($WSUSResult.FileName | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #================================================= # Name #================================================= $WSUSResult.Name = $WSUSResult.Version + ' ' + $WSUSResult.ReleaseID + ' x64 ' + $WSUSResult.Language + ' ' + $WSUSResult.Activation + ' ' + $WSUSResult.Build #================================================= } $ResultsWSUS = $WSUSResults | Where-Object {$_.Version -eq "Windows 10" -and ($_.ReleaseID -eq "21H2" -or $_.ReleaseID -eq "20H2" -or $_.ReleaseID -eq "2004" -or $_.ReleaseID -eq "1909")} | Sort-Object -Property Name #Working on Support for Win 11 22H2 GA ESD Files to support folks who still wanted that version of the ESD file. #$ResultsWSUS = $WSUSResults | Where-Object {(($_.Version -eq "Windows 10") -and ($_.ReleaseID -eq "21H2" -or $_.ReleaseID -eq "20H2" -or $_.ReleaseID -eq "2004" -or $_.ReleaseID -eq "1909")) -or $_.ReleaseID -eq "22H2-GA"} | Sort-Object -Property Name #================================================= # FeatureUpdates ARM64 #================================================= $ARMResults = $ARM64ESDInfo $ARMResults = $ARMResults | Select-Object ` @{Name='Status';Expression={($null)}}, ` @{Name='ReleaseDate';Expression={($null)}}, ` @{Name='Name';Expression={($_.Title)}}, ` @{Name='Version';Expression={($null)}}, ` @{Name='ReleaseID';Expression={($_.null)}}, ` @{Name='Architecture';Expression={($_.Architecture)}}, ` @{Name='Language';Expression={($_.LanguageCode)}}, ` @{Name='Activation';Expression={($null)}}, ` @{Name='Build';Expression={($null)}}, ` @{Name='FileName';Expression={($_.FileName)}}, ` @{Name='ImageIndex';Expression={($null)}}, ` @{Name='ImageName';Expression={($null)}}, ` @{Name='Url';Expression={($_.FilePath)}}, ` @{Name='SHA1';Expression={($_.Sha1)}}, ` @{Name='UpdateID';Expression={($_.UpdateID)}}, ` @{Name='Win10';Expression={($null)}}, ` @{Name='Win11';Expression={($null)}} foreach ($Result in $ARMResults) { #================================================= # Language #================================================= #if ($Result.FileName -match 'sr-latn-rs') { # $Result.Language = 'sr-latn-rs' #} #else { # $Regex = "[a-zA-Z]+-[a-zA-Z]+" # $Result.Language = ($Result.FileName | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #} #================================================= # Activation #================================================= if ($Result.Url -match 'business') { $Result.Activation = 'Volume' } else { $Result.Activation = 'Retail' } #================================================= # Build #================================================= $Regex = "[0-9]*\.[0-9]+" $Result.Build = ($Result.FileName | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #================================================= # OS Version #================================================= if ($Result.Build -lt 22000) { $Result.Version = 'Windows 10' $Result.Win10 = $true $Result.Win11 = $false } if ($Result.Build -ge 22000) { $Result.Version = 'Windows 11' $Result.Win10 = $false $Result.Win11 = $true } #================================================= # ReleaseID #================================================= if ($Result.Build -match "19045"){$Result.ReleaseID = "22H2"} if ($Result.Build -match "22000"){$Result.ReleaseID = "21H2"} if ($Result.Build -match "22621"){$Result.ReleaseID = "22H2"} if ($Result.Build -match "22631"){$Result.ReleaseID = "23H2"} #$Result.ReleaseID = (($Result.FileName).Split(".")[3]).Split("_")[0] #worked on some, not others #================================================= # Date #================================================= $DateString = (($Result.FileName).Split(".")[2]).Split("-")[0] $Date = [datetime]::parseexact($DateString, 'yyMMdd', $null) $Result.ReleaseDate = (Get-Date $Date -Format "yyyy-MM-dd") #================================================= # SHA1 #================================================= #$Regex = "[0-9a-f]{40}" #$Result.SHA1 = ($Result.FileName | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #$Result.SHA1 = ((Split-Path -Leaf $Result.Url) | Select-String -AllMatches -Pattern $Regex).Matches[0].Value #================================================= # Name #================================================= if ($Result.Activation -eq 'Volume') { $Result.Name = $Result.Version + ' ' + $Result.ReleaseID + ' ARM64 ' + $Result.Language + ' Volume ' + $Result.Build } else { $Result.Name = $Result.Version + ' ' + $Result.ReleaseID + ' ARM64 ' + $Result.Language + ' Retail ' + $Result.Build } #================================================= } $ARMResultsMCT = $ARMResults | Sort-Object -Property Name # x64 Catalog File $ResultsTotal += $ResultsMCT $ResultsTotal += $ResultsWSUS $ResultsTotal | Export-Clixml -Path (Join-Path (Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).ModuleBase "Catalogs\CloudOperatingSystems.xml") -Force Import-Clixml -Path (Join-Path (Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).ModuleBase "Catalogs\CloudOperatingSystems.xml") | ConvertTo-Json | Out-File (Join-Path (Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).ModuleBase "Catalogs\CloudOperatingSystems.json") -Encoding ascii -Width 2000 -Force # ARM64 Catalog File $ARMResultsMCT | Export-Clixml -Path (Join-Path (Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).ModuleBase "Catalogs\CloudOperatingSystemsARM64.xml") -Force Import-Clixml -Path (Join-Path (Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).ModuleBase "Catalogs\CloudOperatingSystemsARM64.xml") | ConvertTo-Json | Out-File (Join-Path (Get-Module -Name OSD -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).ModuleBase "Catalogs\CloudOperatingSystemsARM64.json") -Encoding ascii -Width 2000 -Force #================================================ |