private/Install-Software7Zip.ps1
|
#Requires -PSEdition Core function Install-Software7Zip { <# .SYNOPSIS Downloads and installs 7-Zip and prepares its WinPE cache .DESCRIPTION Downloads amd64 and arm64 7-Zip installers with winget to the OSDeployCore software cache. When 7-Zip is not installed, the function can install it on the host with winget. DownloadOnly skips the host installation. The function also removes obsolete 7-Zip WinPE cache versions and populates the current cache with 7zr.exe and the extracted 7z-extra archive when module metadata is available. WhatIf and Confirm apply only to the host installation; downloads, cache directory changes, and WinPE cache preparation are not gated by ShouldProcess. .PARAMETER DownloadOnly Downloads the installers and prepares the WinPE cache without installing 7-Zip on the host. .EXAMPLE PS> Install-Software7Zip Downloads the installers, installs 7-Zip when needed, and prepares the WinPE cache. .EXAMPLE PS> Install-Software7Zip -DownloadOnly Downloads and caches 7-Zip content without installing the host application. .INPUTS None. This function does not accept pipeline input. .OUTPUTS System.String. Returns native winget output written to the success stream. System.Management.Automation.PSCustomObject. Returns installation and cache status. .NOTES Author: David Segura Company: Recast Software Version: 1.0.0 Date: 2026-08-28 Requires Windows and winget. WinPE cache downloads require curl.exe or Invoke-WebRequest. The function reads 7-Zip package and WinPE app settings from $global:OSDeployModule when available. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] [OutputType([pscustomobject])] param ( [Parameter()] [switch] $DownloadOnly ) if (-not $IsWindows) { throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Install-Software7Zip is supported only on Windows." } $winget = Get-Command -Name 'winget' -ErrorAction SilentlyContinue if (-not $winget) { throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] winget is required but was not found. Install App Installer from Microsoft Store and try again." } $packageId = '7zip.7zip' if ($global:OSDeployModule -and $global:OSDeployModule.Software.'7zip'.id) { $packageId = [string]$global:OSDeployModule.Software.'7zip'.id } #region Download installer for both architectures $softwarePath = Join-Path $script:OSDeployCoreSoftwarePath $packageId $amd64Path = Join-Path $softwarePath 'amd64' $arm64Path = Join-Path $softwarePath 'arm64' foreach ($archEntry in @( @{ Path = $amd64Path; Arch = 'x64'; Label = 'amd64' } @{ Path = $arm64Path; Arch = 'arm64'; Label = 'arm64' } )) { $archPath = $archEntry.Path $archArg = $archEntry.Arch $archLabel = $archEntry.Label $alreadyCached = (Test-Path -Path $archPath) -and (Get-ChildItem -Path $archPath -File -ErrorAction SilentlyContinue | Select-Object -First 1) if ($alreadyCached) { Write-Host "[$(Get-Date -Format s)] 7-Zip installer already cached ($archLabel): $archPath" -ForegroundColor Green } else { if (-not (Test-Path -Path $archPath)) { New-Item -Path $archPath -ItemType Directory -Force | Out-Null } Write-Host "[$(Get-Date -Format s)] Downloading 7-Zip installer ($archLabel) to $archPath..." -ForegroundColor DarkGray & $winget.Source download --id $packageId --exact --download-directory $archPath --architecture $archArg --accept-source-agreements --accept-package-agreements if ($LASTEXITCODE -ne 0) { Write-Warning "[$(Get-Date -Format s)] winget download exited with code $LASTEXITCODE for architecture $archLabel." } } } #endregion #region Install 7-Zip on host (skip when -DownloadOnly) $wasInstalled = $false $skippedInstall = $false if ($DownloadOnly) { Write-Host "[$(Get-Date -Format s)] -DownloadOnly specified — skipping host installation." -ForegroundColor DarkGray $skippedInstall = $true } else { $sevenZipExe = Join-Path $env:ProgramFiles '7-Zip\7z.exe' if (Test-Path -Path $sevenZipExe) { $version = (& $sevenZipExe i 2>&1 | Select-String -Pattern '^\d' | Select-Object -First 1).ToString().Trim() Write-Host "[$(Get-Date -Format s)] 7-Zip is already installed: $version" -ForegroundColor Green } else { if ($PSCmdlet.ShouldProcess($packageId, 'Install 7-Zip using winget')) { Write-Host "[$(Get-Date -Format s)] 7-Zip is not installed. Installing with winget..." -ForegroundColor DarkGray & $winget.Source install --id $packageId --exact -e -h --accept-source-agreements --accept-package-agreements if ($LASTEXITCODE -ne 0) { throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] 7-Zip installation failed with exit code $LASTEXITCODE." } $wasInstalled = $true if (-not (Test-Path -Path $sevenZipExe)) { Write-Warning "[$(Get-Date -Format s)] 7-Zip was installed but 7z.exe was not found at '$sevenZipExe'." } } } } #endregion #region Populate WinPE apps cache (7zr.exe + 7z-extra archive) # Mirrors Step-BuildBootInstallWinPEAppZip so the boot image build finds the files already cached. if ($global:OSDeployModule -and $global:OSDeployModule.BootImage.winpeapps.sevenzip) { $zipConfig = $global:OSDeployModule.BootImage.winpeapps.sevenzip $zipVersion = $zipConfig.version $CacheZipRoot = Join-Path $Script:OSDeployCorePath 'cache' 'winpe-apps' '7zip' $CacheZip = Join-Path $CacheZipRoot $zipVersion # Cleanup old version directories if (Test-Path -Path $CacheZipRoot) { Get-ChildItem -Path $CacheZipRoot -File -ErrorAction SilentlyContinue | Remove-Item -Force Get-ChildItem -Path $CacheZipRoot -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -ne $zipVersion } | Remove-Item -Recurse -Force } if (Test-Path -Path $CacheZip) { Write-Host "[$(Get-Date -Format s)] Using 7-Zip WinPE cache: $CacheZip" -ForegroundColor Green } else { Write-Host "[$(Get-Date -Format s)] Creating 7-Zip WinPE cache: $CacheZip" -ForegroundColor DarkGray New-Item -Path $CacheZip -ItemType Directory -Force | Out-Null } # Download 7zr.exe (standalone, used to extract the extra archive) $sevenZrPath = Join-Path $CacheZip '7zr.exe' if (-not (Test-Path -Path $sevenZrPath)) { $downloadUrl = [string]$zipConfig.standalone Write-Host "[$(Get-Date -Format s)] Downloading 7zr.exe from $downloadUrl" -ForegroundColor DarkGray if (Get-Command 'curl.exe' -ErrorAction SilentlyContinue) { & curl.exe --silent --location --retry 5 --continue-at - --output $sevenZrPath $downloadUrl } else { Invoke-WebRequest -UseBasicParsing -Uri $downloadUrl -OutFile $sevenZrPath } } else { Write-Host "[$(Get-Date -Format s)] 7zr.exe already cached: $sevenZrPath" -ForegroundColor Green } # Download and extract the 7z-extra archive $extraDir = Join-Path $CacheZip '7za' if (-not (Test-Path -Path $extraDir)) { $downloadUrl = [string]$zipConfig.extra $extraFileName = Split-Path $downloadUrl -Leaf $downloadPath = Join-Path $CacheZip $extraFileName Write-Host "[$(Get-Date -Format s)] Downloading 7-Zip extra archive: $extraFileName" -ForegroundColor DarkGray if (Get-Command 'curl.exe' -ErrorAction SilentlyContinue) { & curl.exe --silent --location --retry 5 --continue-at - --output $downloadPath $downloadUrl } else { Invoke-WebRequest -UseBasicParsing -Uri $downloadUrl -OutFile $downloadPath } Write-Host "[$(Get-Date -Format s)] Extracting $extraFileName to $extraDir" -ForegroundColor DarkGray $null = & $sevenZrPath x $downloadPath -o"$extraDir" -y } else { Write-Host "[$(Get-Date -Format s)] 7-Zip extra archive already extracted: $extraDir" -ForegroundColor Green } } else { Write-Verbose "[$($MyInvocation.MyCommand.Name)] OSDeployModule or BootImage.winpeapps.sevenzip not available — skipping WinPE cache preparation." } #endregion [pscustomobject]@{ Component = '7-Zip' PackageId = $packageId WasInstalled = $wasInstalled SkippedInstall = $skippedInstall DownloadOnly = $DownloadOnly.IsPresent SoftwarePath = $softwarePath } } |