Public/Get-OSDAppCache.ps1
|
function Get-OSDAppCache { [CmdletBinding()] param( [string[]]$Name, [switch]$ResolveVersions, [switch]$VerifySignatures ) # Cache inspection is optional when no OSDCloud media is attached. try { $cachePath = Get-OSDAppCachePath } catch { Write-Verbose 'No OSDCloud cache volume is available.' return } $results = [System.Collections.Generic.List[object]]::new() $cacheCatalogPath = Join-Path $cachePath 'CacheCatalog.json' if (Test-Path -LiteralPath $cacheCatalogPath -PathType Leaf) { try { $catalog = Get-OSDAppManifest -Path $cacheCatalogPath foreach ($package in @($catalog.Packages)) { $archivePath = Join-Path $cachePath (Join-Path 'Packages' (Join-Path $package.Id 'Package.zip')) $exists = Test-Path -LiteralPath $archivePath -PathType Leaf $valid = $false $sizeMB = $null if ($exists) { $sizeMB = [math]::Round((Get-Item -LiteralPath $archivePath).Length / 1MB, 1) if ($package.Archive -and $package.Archive.Sha256) { $valid = Test-OSDAppFileHash -Path $archivePath -ExpectedSha256 $package.Archive.Sha256 } } $results.Add([pscustomobject]@{ PSTypeName = 'OSDApps.CacheEntry' Id = [string]$package.Id DisplayName = if ($package.DisplayName) { [string]$package.DisplayName } else { [string]$package.Id } Vendor = if ($package.Vendor) { [string]$package.Vendor } else { $null } Source = 'Repository' Version = [string]$package.Version Architecture = [string]$package.Architecture Channel = $null Language = $null SourcePolicy = 'RepositoryManaged' SyncMethod = 'ManifestSha256' LastSynced = $catalog.GeneratedAt SizeMB = $sizeMB Valid = $valid CachePath = if ($exists) { $archivePath } else { Join-Path $cachePath (Join-Path 'Packages' $package.Id) } SignatureStatus = 'Not assessed' SignatureScope = 'Repository ZIP (SHA-256 checked separately)' SignatureFile = $null SignatureSigner = $null SignatureDetail = 'Authenticode is not evaluated for custom repository ZIPs.' }) } } catch { Write-Warning "Failed to read repository cache catalog '$cacheCatalogPath': $($_.Exception.Message)" } } $builtInRoot = Join-Path $cachePath 'BuiltIn' if (Test-Path -LiteralPath $builtInRoot -PathType Container) { foreach ($cacheInfoFile in @(Get-ChildItem -LiteralPath $builtInRoot -Filter 'CacheInfo.json' -File -Recurse -ErrorAction SilentlyContinue)) { try { $info = Get-Content -LiteralPath $cacheInfoFile.FullName -Raw -Encoding UTF8 | ConvertFrom-Json $entryRoot = Split-Path -Path $cacheInfoFile.FullName -Parent $files = @(Get-ChildItem -LiteralPath $entryRoot -File -Recurse -ErrorAction SilentlyContinue) $bytes = ($files | Measure-Object -Property Length -Sum).Sum if ($null -eq $bytes) { $bytes = 0 } $payloadValid = switch ([string]$info.Id) { 'Microsoft365Apps' { (Test-Path -LiteralPath (Join-Path $entryRoot 'setup.exe') -PathType Leaf) -and (Test-Path -LiteralPath (Join-Path $entryRoot 'configuration.xml') -PathType Leaf) -and (Test-Path -LiteralPath (Join-Path $entryRoot 'Office\Data') -PathType Container) } 'MicrosoftTeams' { (Test-Path -LiteralPath (Join-Path $entryRoot 'teamsbootstrapper.exe') -PathType Leaf) -and (Test-Path -LiteralPath (Join-Path $entryRoot 'teams.msix') -PathType Leaf) } 'AdobeAcrobatUnified' { Test-Path -LiteralPath (Join-Path $entryRoot 'Package.zip') -PathType Leaf } 'GoogleChromeEnterprise' { Test-Path -LiteralPath (Join-Path $entryRoot 'Package.msi') -PathType Leaf } 'CiscoWebex' { Test-Path -LiteralPath (Join-Path $entryRoot 'Package.msi') -PathType Leaf } 'MozillaFirefoxEnterprise' { Test-Path -LiteralPath (Join-Path $entryRoot 'Package.msi') -PathType Leaf } default { $files.Count -gt 0 } } $cachedVersion = if ($null -ne $info.Version) { ([string]$info.Version).Trim() } else { $null } $resolvedVersion = if ($cachedVersion -and $cachedVersion -ne 'Current') { $cachedVersion } else { $null } $versionSource = if ($info.VersionSource) { [string]$info.VersionSource } else { $null } if ($ResolveVersions -and $payloadValid -and -not (Test-OSDAppWinPE) -and -not $resolvedVersion) { $inspected = Get-OSDAppCachedPackageVersion -Id ([string]$info.Id) -CacheEntryPath $entryRoot if ($inspected.Version) { $resolvedVersion = [string]$inspected.Version $versionSource = [string]$inspected.VersionSource } } # Signature inspection is opt-in. It is separate from Valid: # Valid still describes required file presence, never signer trust. $signature = $null if ($VerifySignatures -and $payloadValid) { $signature = Get-OSDAppBuiltInSignature -Id ([string]$info.Id) -CacheEntryPath $entryRoot -CheckPublisher } $results.Add([pscustomobject]@{ PSTypeName = 'OSDApps.CacheEntry' Id = [string]$info.Id Source = 'BuiltIn' Version = if ($resolvedVersion) { $resolvedVersion } else { $cachedVersion } VersionSource = $versionSource Architecture = if ($info.Architecture) { [string]$info.Architecture } else { $null } Channel = if ($info.Channel) { [string]$info.Channel } else { $null } Language = if ($info.Language -is [System.Collections.IEnumerable] -and $info.Language -isnot [string]) { @($info.Language) -join ',' } elseif ($info.Language) { [string]$info.Language } else { $null } SourcePolicy = if ($info.SourcePolicy) { [string]$info.SourcePolicy } else { 'Evergreen' } SyncMethod = if ($info.SyncMethod) { [string]$info.SyncMethod } else { $null } LastSynced = $info.SyncedAt SizeMB = [math]::Round([double]$bytes / 1MB, 1) Valid = [bool]$payloadValid CachePath = $entryRoot SignatureStatus = if (-not $payloadValid) { 'File missing' } elseif ($signature) { [string]$signature.Status } else { 'Not checked' } SignatureScope = if ($signature) { [string]$signature.Scope } else { $null } SignatureFile = if ($signature) { $signature.File } else { $null } SignatureSigner = if ($signature) { $signature.Signer } else { $null } SignatureDetail = if ($signature) { $signature.Detail } else { $null } }) } catch { Write-Warning "Failed to inspect built-in cache '$($cacheInfoFile.FullName)': $($_.Exception.Message)" } } } $output = @($results) if ($Name) { $output = @($output | Where-Object { $entry = $_ @($Name | Where-Object { $entry.Id -like $_ }).Count -gt 0 }) } $output | Sort-Object Source, Id, Architecture, Channel, Language } |