private/core-cache/Get-OSDCoreCacheContent.ps1
|
function Get-OSDCoreCacheContent { <# .SYNOPSIS Returns cached OSDCloud content found on local file system drives. .DESCRIPTION Enumerates mounted file system drives and discovers OSDCloud cache content. Returns objects with Type, Name, FullName, SizeMB, DriveRoot, VolumeLabel, VolumeUniqueId, and USB properties. Exports the returned object to $env:Temp\OSDCoreCache.xml each time the function runs. If Type is omitted, returns all supported cache content types. Type values: - ESD: All .esd files under '<DriveLetter>:\OSDCloud\OS' recursively. - ISO: All .iso files under '<DriveLetter>:\OSDCloud\ISO' recursively. - DriverPacks: All .cab, .exe, .msi, and .zip files under '<DriveLetter>:\OSDCloud\DriverPacks' recursively. - Drivers: Immediate folders under '<DriveLetter>:\OSDCloud\Drivers' that contain at least one .inf file in any child folder. - ModelDrivers: Immediate folders under 'OSDCloud\modeldrivers-amd64' and 'OSDCloud\modeldrivers-arm64' containing .inf files recursively. Names use '<osdmanufacturer>_<osdproduct>_<osdmodel>_<osbuild>'. Model text is descriptive; deployment matches manufacturer/product and selects the highest numeric folder build, regardless of the deployed OS build. - WinPEDrivers: Immediate folders under 'OSDCloud\winpedrivers-amd64' and 'OSDCloud\winpedrivers-arm64' with the same naming and INF requirements as ModelDrivers. Discovered for inventory only; not used in deployment. - Profiles: Immediate folders under '<DriveLetter>:\OSDCloud\Profiles'. - WIM: All .wim files under '<DriveLetter>:\OSDCloud\WIM' recursively. - *: Includes all supported Type values. .PARAMETER Type Optional cache content selector. Supports one or more values. Use '*' to return all supported cache content types. .PARAMETER Include Optional list of drive letters to include when searching for OSDCloud cache content. Accepts values such as 'C', 'D:', or 'E:\'. When omitted, all mounted file system drive letters are considered. .PARAMETER Exclude Optional list of drive letters to exclude when searching for OSDCloud cache content. Accepts values such as 'C', 'D:', or 'E:\'. Excluded drives are skipped even when they are also present in Include. .OUTPUTS System.Object[]. Objects with Type, Name, FullName, SizeMB, DriveRoot, VolumeLabel, VolumeUniqueId, and USB. ModelDrivers and WinPEDrivers also include OSArchitecture, OSBuildVersion, and ModelIdentity (the folder name without its trailing build). .NOTES ModelDrivers discovery does not guarantee that a source is safe for deployment. Deployment excludes sources on disks that will be cleared. .EXAMPLE Get-OSDCoreCacheContent Returns all supported cache content types. .EXAMPLE Get-OSDCoreCacheContent -Type ESD Returns all .esd files under each discovered cache OS folder. .EXAMPLE Get-OSDCoreCacheContent -Type ESD,DriverPacks Returns all .esd files and driver pack files from each discovered cache. .EXAMPLE Get-OSDCoreCacheContent -Type ModelDrivers Returns model-specific driver folders for both architectures. .EXAMPLE Get-OSDCoreCacheContent -Type WinPEDrivers Returns cached WinPE driver folders for both architectures. .EXAMPLE Get-OSDCoreCacheContent -Type * Returns all supported cache content types. .EXAMPLE Get-OSDCoreCacheContent -Include C,D -Exclude D Searches only drive C for supported cache content types. #> [CmdletBinding()] [OutputType([System.Object[]])] param ( [Parameter()] [ValidateSet('ESD', 'ISO', 'DriverPacks', 'Drivers', 'ModelDrivers', 'WinPEDrivers', 'Profiles', 'WIM', '*')] [string[]]$Type, [Parameter()] [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] [string[]]$Include, [Parameter()] [ValidatePattern('^[a-zA-Z](:\\)?$|^[a-zA-Z]$')] [string[]]$Exclude ) $Error.Clear() Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" # Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] OSDCoreCacheContent is updating" function Get-FileOnlySizeMB { param( [Parameter(Mandatory)] [string]$Path ) if (-not (Test-Path -LiteralPath $Path)) { return 0 } $item = Get-Item -LiteralPath $Path -ErrorAction SilentlyContinue if (-not $item) { return 0 } if ($item.PSIsContainer) { $totalBytes = (Get-ChildItem -LiteralPath $Path -Recurse -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum if ($null -eq $totalBytes) { $totalBytes = 0 } return [math]::Round(($totalBytes / 1MB), 2) } return [math]::Round((([int64]$item.Length) / 1MB), 2) } function Get-DriveVolumeMetadata { param( [Parameter(Mandatory)] [string]$DriveRoot ) $volume = $null $usb = $false if ($DriveRoot -match '^[A-Z]:\\$') { try { $driveLetter = $DriveRoot.Substring(0, 1) $volume = Get-Volume -DriveLetter $driveLetter -ErrorAction Stop $usb = [bool](Get-Disk -ErrorAction SilentlyContinue | Where-Object { $_.BusType -eq 'USB' } | Get-Partition -ErrorAction SilentlyContinue | Where-Object { $_.DriveLetter -eq $driveLetter }) } catch { $volume = $null $usb = $false } } return [PSCustomObject]@{ DriveRoot = $DriveRoot VolumeLabel = if ($volume) { [string]$volume.FileSystemLabel } else { $null } VolumeUniqueId = if ($volume) { [string]$volume.UniqueId } else { $null } USB = [bool]$usb } } function New-CacheResultObject { param( [Parameter(Mandatory)] [string]$ResultType, [Parameter(Mandatory)] [string]$ResultFullName, [Parameter(Mandatory)] $VolumeMetadata ) [PSCustomObject]@{ Type = $ResultType Name = Split-Path -Path $ResultFullName -Leaf FullName = $ResultFullName SizeMB = Get-FileOnlySizeMB -Path $ResultFullName USB = [bool]$VolumeMetadata.USB DriveRoot = [string]$VolumeMetadata.DriveRoot VolumeLabel = [string]$VolumeMetadata.VolumeLabel VolumeUniqueId = [string]$VolumeMetadata.VolumeUniqueId } } function ConvertTo-DriveLetterList { param( [Parameter()] [string[]]$DriveLetters ) $normalized = foreach ($driveLetter in $DriveLetters) { if ([string]::IsNullOrWhiteSpace($driveLetter)) { continue } $value = $driveLetter.Trim().TrimEnd('\\') if ($value.Length -lt 1) { continue } $letter = $value.Substring(0, 1).ToUpperInvariant() if ($letter -match '^[A-Z]$') { $letter } } @($normalized | Sort-Object -Unique) } $includedDriveLetters = ConvertTo-DriveLetterList -DriveLetters $Include $excludedDriveLetters = ConvertTo-DriveLetterList -DriveLetters $Exclude $cachePaths = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Root -match '^[A-Z]:\\$' } | Where-Object { $driveLetter = ([string]$_.Root).Substring(0, 1).ToUpperInvariant() (($includedDriveLetters.Count -eq 0) -or ($driveLetter -in $includedDriveLetters)) -and ($driveLetter -notin $excludedDriveLetters) } | ForEach-Object { $driveRoot = [string]$_.Root $osdCloudPath = Join-Path -Path $driveRoot -ChildPath 'OSDCloud' if (Test-Path -LiteralPath $osdCloudPath) { $volumeMetadata = Get-DriveVolumeMetadata -DriveRoot $driveRoot [PSCustomObject]@{ CachePath = $osdCloudPath VolumeMetadata = $volumeMetadata } } } $cachePaths = @($cachePaths | Sort-Object -Property CachePath -Unique) $selectedTypes = if ($PSBoundParameters.ContainsKey('Type')) { @($Type | Sort-Object -Unique) } else { @('ESD', 'ISO', 'DriverPacks', 'Drivers', 'ModelDrivers', 'WinPEDrivers', 'Profiles', 'WIM') } if ($selectedTypes -contains '*') { $selectedTypes = @('ESD', 'ISO', 'DriverPacks', 'Drivers', 'ModelDrivers', 'WinPEDrivers', 'Profiles', 'WIM') } $result = foreach ($selectedType in $selectedTypes) { switch ($selectedType) { 'ESD' { foreach ($cacheEntry in $cachePaths) { $osPath = Join-Path -Path $cacheEntry.CachePath -ChildPath 'OS' if (Test-Path -LiteralPath $osPath) { Get-ChildItem -LiteralPath $osPath -Recurse -File -Filter '*.esd' -ErrorAction SilentlyContinue | ForEach-Object { New-CacheResultObject -ResultType 'ESD' -ResultFullName ([string]$_.FullName) -VolumeMetadata $cacheEntry.VolumeMetadata } } } break } 'ISO' { foreach ($cacheEntry in $cachePaths) { $isoPath = Join-Path -Path $cacheEntry.CachePath -ChildPath 'ISO' if (Test-Path -LiteralPath $isoPath) { Get-ChildItem -LiteralPath $isoPath -Recurse -File -Filter '*.iso' -ErrorAction SilentlyContinue | ForEach-Object { New-CacheResultObject -ResultType 'ISO' -ResultFullName ([string]$_.FullName) -VolumeMetadata $cacheEntry.VolumeMetadata } } } break } 'DriverPacks' { foreach ($cacheEntry in $cachePaths) { $driverPacksPath = Join-Path -Path $cacheEntry.CachePath -ChildPath 'DriverPacks' if (Test-Path -LiteralPath $driverPacksPath) { Get-ChildItem -LiteralPath $driverPacksPath -Recurse -File -ErrorAction SilentlyContinue | Where-Object { $_.Extension -in @('.cab', '.exe', '.msi', '.zip') } | ForEach-Object { New-CacheResultObject -ResultType 'DriverPacks' -ResultFullName ([string]$_.FullName) -VolumeMetadata $cacheEntry.VolumeMetadata } } } break } 'Drivers' { foreach ($cacheEntry in $cachePaths) { $driversPath = Join-Path -Path $cacheEntry.CachePath -ChildPath 'Drivers' if (Test-Path -LiteralPath $driversPath) { Get-ChildItem -LiteralPath $driversPath -Directory -ErrorAction SilentlyContinue | Where-Object { @(Get-ChildItem -LiteralPath $_.FullName -Recurse -File -Filter '*.inf' -ErrorAction SilentlyContinue).Count -gt 0 } | ForEach-Object { New-CacheResultObject -ResultType 'Drivers' -ResultFullName ([string]$_.FullName) -VolumeMetadata $cacheEntry.VolumeMetadata } } } break } { $_ -in @('ModelDrivers', 'WinPEDrivers') } { $driverType = if ($selectedType -eq 'ModelDrivers') { 'ModelDrivers' } else { 'WinPEDrivers' } foreach ($cacheEntry in $cachePaths) { foreach ($architecture in 'amd64', 'arm64') { $architectureDriversPath = Join-Path -Path $cacheEntry.CachePath -ChildPath "$($driverType.ToLowerInvariant())-$architecture" if (-not (Test-Path -LiteralPath $architectureDriversPath -PathType Container)) { continue } foreach ($folder in (Get-ChildItem -LiteralPath $architectureDriversPath -Directory -ErrorAction Stop)) { $buildVersion = $null if ($folder.Name -notmatch '^(?<Identity>[^_]+_.+_.+)_(?<Build>\d+\.\d+)$' -or -not [version]::TryParse($Matches.Build, [ref]$buildVersion)) { Write-Warning "[$(Get-Date -format s)] Invalid $driverType folder name: $($folder.FullName)" continue } $modelIdentity = $Matches.Identity $infFiles = @(Get-ChildItem -LiteralPath $folder.FullName -Recurse -File -Filter '*.inf' -ErrorAction Stop) if ($infFiles.Count -eq 0) { continue } $cacheObject = New-CacheResultObject -ResultType $driverType -ResultFullName $folder.FullName -VolumeMetadata $cacheEntry.VolumeMetadata $cacheObject | Add-Member -NotePropertyName OSArchitecture -NotePropertyValue $architecture $cacheObject | Add-Member -NotePropertyName OSBuildVersion -NotePropertyValue $buildVersion.ToString() $cacheObject | Add-Member -NotePropertyName ModelIdentity -NotePropertyValue $modelIdentity $cacheObject } } } break } 'Profiles' { foreach ($cacheEntry in $cachePaths) { $profilesPath = Join-Path -Path $cacheEntry.CachePath -ChildPath 'Profiles' if (Test-Path -LiteralPath $profilesPath) { Get-ChildItem -LiteralPath $profilesPath -Directory -ErrorAction SilentlyContinue | ForEach-Object { New-CacheResultObject -ResultType 'Profiles' -ResultFullName ([string]$_.FullName) -VolumeMetadata $cacheEntry.VolumeMetadata } } } break } 'WIM' { foreach ($cacheEntry in $cachePaths) { $wimPath = Join-Path -Path $cacheEntry.CachePath -ChildPath 'WIM' if (Test-Path -LiteralPath $wimPath) { Get-ChildItem -LiteralPath $wimPath -Recurse -File -Filter '*.wim' -ErrorAction SilentlyContinue | ForEach-Object { New-CacheResultObject -ResultType 'WIM' -ResultFullName ([string]$_.FullName) -VolumeMetadata $cacheEntry.VolumeMetadata } } } break } } } $result = @($result | Sort-Object -Property FullName, Type -Unique | Sort-Object -Property Type, FullName) $result | Export-Clixml -Path (Join-Path -Path $env:Temp -ChildPath 'OSDCoreCache.xml') -Force Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found $($result.Count) path(s)" Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" return $result } |