Public/Add-OSDApp.ps1
|
function Add-OSDApp { [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory, Position=0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('Id')] [string[]]$Name, [string]$WindowsPath, [switch]$SkipCacheRefresh ) begin { $requested=[System.Collections.Generic.List[string]]::new() } process { foreach($item in $Name){ if(-not [string]::IsNullOrWhiteSpace($item)){ $requested.Add($item) } } } end { $apps=@($requested | Select-Object -Unique) if($apps.Count -eq 0){throw 'No applications were supplied.'} foreach($app in $apps){ if($app -ieq 'Microsoft365Apps'){throw "'Microsoft365Apps' is a built-in application. Use Add-OSDAppMicrosoft365Apps instead."} if($app -ieq 'MicrosoftTeams' -or $app -ieq 'Teams'){throw "'MicrosoftTeams' is a built-in application. Use Add-OSDAppMicrosoftTeams instead."} if($app -ieq 'AdobeAcrobatUnified'){throw "'AdobeAcrobatUnified' is a built-in application. Use Add-OSDAppAdobeAcrobatUnified instead."} if($app -ieq 'GoogleChromeEnterprise'){throw "'GoogleChromeEnterprise' is a built-in application. Use Add-OSDAppGoogleChromeEnterprise instead."} if($app -ieq 'MozillaFirefoxEnterprise'){throw "'MozillaFirefoxEnterprise' is a built-in application. Use Add-OSDAppMozillaFirefoxEnterprise instead."} if($app -ieq 'CiscoWebex'){throw "'CiscoWebex' is a built-in application. Use Add-OSDAppCiscoWebex instead."} } # USB cache is optional. When absent, stage repository archives in # a temporary cache on the offline Windows disk instead. $cachePath = $null try { $cachePath = Get-OSDAppCachePath } catch { Write-Verbose 'OSDCloud cache not available; using the offline Windows disk.' } $resolvedWindowsPath = Resolve-OSDAppWindowsPath -WindowsPath $WindowsPath if (-not $cachePath) { $cachePath = Join-Path $resolvedWindowsPath 'Windows\Temp\OSDApps\RepositoryCache' Write-Verbose ("Local repository cache: {0}" -f $cachePath) } # WhatIf must not download, create a local cache, modify the manifest, # or update SetupComplete.cmd. if (-not $PSCmdlet.ShouldProcess(($apps -join ', '), "Stage repository applications for SetupComplete on $resolvedWindowsPath")) { return } $sourceUri = (Get-OSDAppConfiguration).CatalogUri $cacheCatalogPath = Join-Path $cachePath 'CacheCatalog.json' if ($sourceUri -and -not $SkipCacheRefresh) { if ($VerbosePreference -ne 'SilentlyContinue') { Write-OSDAppConsole -Level Info -Component 'Repository' -Message ("Synchronizing requested repository application(s): {0}" -f ($apps -join ', ')) } try { Sync-OSDAppCache -CatalogUri $sourceUri -CachePath $cachePath -Name $apps -Confirm:$false -ErrorAction Stop | Out-Null } catch { $syncError = $_.Exception.Message # Only fall back when *every* requested archive is present and # matches the SHA-256 recorded in the local cache catalog. # A partial or corrupted cache must never be staged. $validCache = @() if (Test-Path -LiteralPath $cacheCatalogPath -PathType Leaf) { try { $validCache = @(Test-OSDAppCache -CachePath $cachePath -Name $apps -ErrorAction Stop) } catch { Write-Verbose "Offline cache validation failed: $($_.Exception.Message)" } } $unavailable = @($apps | Where-Object { $requestedName = $_ @($validCache | Where-Object { $_.Id -ieq $requestedName -and $_.Valid }).Count -ne 1 }) if ($unavailable.Count -gt 0) { throw "Repository synchronization failed ($syncError). No valid offline cache for: $($unavailable -join ', ')." } Write-Warning "Repository synchronization failed ($syncError). Using SHA-256 validated offline cache for: $($apps -join ', ')." } if ($VerbosePreference -ne 'SilentlyContinue') { Write-OSDAppConsole -Level Success -Component 'Repository' -Message 'Requested repository application cache is available' } } else { if (-not (Test-Path -LiteralPath $cacheCatalogPath -PathType Leaf)) { throw 'No cached repository catalog is available. Configure CatalogUri and synchronize first, or attach a USB drive with a populated OSDApps cache.' } $cachedPackages = @(Test-OSDAppCache -CachePath $cachePath -Name $apps) $unavailable = @($apps | Where-Object { $requestedName = $_ @($cachedPackages | Where-Object { $_.Id -ieq $requestedName -and $_.Valid }).Count -ne 1 }) if ($unavailable.Count -gt 0) { throw "No valid offline cache for: $($unavailable -join ', ')." } Write-Verbose 'Using SHA-256 validated offline repository cache without synchronization.' } $stagedRelativePath='Windows\Temp\OSDApps' Copy-OSDAppContent -Name $apps -CachePath $cachePath -WindowsPath $resolvedWindowsPath -DestinationRelativePath $stagedRelativePath -Confirm:$false -ErrorAction Stop | Out-Null $manifestPath=Join-Path (Join-Path $resolvedWindowsPath $stagedRelativePath) 'DeviceManifest.json' $manifest=Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8 | ConvertFrom-Json $manifest = Set-OSDAppManifestRuntimeConfiguration -Manifest $manifest $manifest | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $manifestPath -Encoding UTF8 Add-OSDAppSetupComplete -WindowsPath $resolvedWindowsPath -StagedRelativePath $stagedRelativePath -Confirm:$false -ErrorAction Stop | Out-Null foreach($app in $apps){[pscustomobject]@{PSTypeName='OSDApps.StagedApp';Name=$app;CachePath=$cachePath;WindowsPath=$resolvedWindowsPath;StagedPath=(Join-Path $resolvedWindowsPath $stagedRelativePath);Source='Repository'}} } } |