Examples/Invoke-JuribaAppRSelfService.ps1
|
<# .SYNOPSIS Interactive self-service packaging for Juriba App Readiness. .DESCRIPTION A console-driven replacement for the HTML/JS self-service portal. Mirrors the portal's workflow end-to-end using only the Juriba.AppR PowerShell module: 1. Connect to the App Readiness instance. 2. Choose a source: KB - Search the Knowledge Base, pick an application/version/file, download it locally, then upload. File - Point at an existing installer on disk. 3. Upload the installer (chunked). 4. Create the application - server-side metadata extraction and command suggestion are used automatically by New-JuribaAppRApplication. 5. Wait for the application ID to resolve. 6. Watch packaging status through to a terminal state. The script can be driven interactively (prompted menus) or non-interactively by passing -SearchTerm or -SetupFilePath. .PARAMETER InstanceUrl Base URL of the App Readiness instance. Not required if already connected via Connect-JuribaAppR. .PARAMETER APIKey API key. Not required if already connected. .PARAMETER SearchTerm Non-interactive: search the KB for this term and auto-select the top result and latest version/x64-preferred source. .PARAMETER SetupFilePath Non-interactive: skip the KB, upload this local installer directly. .PARAMETER IntervalSeconds Packaging status poll interval. Default 60. .PARAMETER TimeoutMinutes Packaging status timeout. Default 60. .EXAMPLE .\Invoke-JuribaAppRSelfService.ps1 -InstanceUrl "https://sandbox.appr.juriba.app" -APIKey $key Fully interactive mode. .EXAMPLE .\Invoke-JuribaAppRSelfService.ps1 -InstanceUrl "https://sandbox.appr.juriba.app" -APIKey $key -SearchTerm "7-Zip" Non-interactive KB run. .EXAMPLE .\Invoke-JuribaAppRSelfService.ps1 -InstanceUrl "https://sandbox.appr.juriba.app" -APIKey $key -SetupFilePath "C:\Installers\Firefox-Setup.exe" Non-interactive local-file run. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Interactive example script - user-facing colored console output for progress and results.')] [CmdletBinding(DefaultParameterSetName = 'Interactive')] param ( [Parameter(Mandatory = $false)] [string]$InstanceUrl, [Parameter(Mandatory = $false)] [string]$APIKey, [Parameter(ParameterSetName = 'KB')] [string]$SearchTerm, [Parameter(ParameterSetName = 'Local')] [string]$SetupFilePath, [int]$IntervalSeconds = 60, [int]$TimeoutMinutes = 60 ) $ErrorActionPreference = 'Stop' # --- Import module ----------------------------------------------------------- $modulePath = Join-Path (Join-Path $PSScriptRoot '..') 'Juriba.AppR.psd1' if (-not (Test-Path $modulePath)) { Write-Error "Cannot find Juriba.AppR.psd1 at $modulePath. Run this script from its own folder." } Import-Module $modulePath -Force Write-Host "Juriba.AppR module imported" -ForegroundColor Cyan # --- Connect ----------------------------------------------------------------- $connectedHere = $false $existing = Get-JuribaAppRSession $activeInstance = $null if ($existing -and -not $InstanceUrl) { $activeInstance = $existing.Instance Write-Host "Using existing session: $activeInstance" -ForegroundColor Green } else { if (-not $InstanceUrl) { $InstanceUrl = Read-Host "Instance URL (e.g. https://sandbox.appr.juriba.app)" } if (-not $APIKey) { $APIKey = Read-Host "API key" -MaskInput } Connect-JuribaAppR -Instance $InstanceUrl -APIKey $APIKey $activeInstance = $InstanceUrl Write-Host "Connected to $activeInstance" -ForegroundColor Green $connectedHere = $true } try { # --- Choose source ------------------------------------------------------- $mode = switch ($PSCmdlet.ParameterSetName) { 'KB' { 'kb' } 'Local' { 'local' } default { Write-Host "" Write-Host "Choose a source:" -ForegroundColor Cyan Write-Host " [1] Search the Knowledge Base" Write-Host " [2] Upload a local installer" $choice = Read-Host "Select (1-2)" if ($choice -eq '2') { 'local' } else { 'kb' } } } $localPath = $null $metaHint = $null # KB-provided metadata hints (name, manufacturer, version) $cleanupTempDownload = $false # --- KB source ----------------------------------------------------------- if ($mode -eq 'kb') { if (-not $SearchTerm) { $SearchTerm = Read-Host "Search term" } Write-Host "`n=== Searching Knowledge Base for '$SearchTerm' ===" -ForegroundColor Magenta $kbResults = @(Search-JuribaAppRKnowledgeBase -Search $SearchTerm) if (-not $kbResults.Count) { throw "No Knowledge Base matches for '$SearchTerm'. Try the -SetupFilePath path instead." } Write-Host "Found $($kbResults.Count) application(s):" for ($i = 0; $i -lt $kbResults.Count; $i++) { Write-Host (" [{0}] {1} ({2})" -f ($i + 1), $kbResults[$i].applicationName, $kbResults[$i].vendorName) } $appChoice = if ($kbResults.Count -eq 1 -or $PSCmdlet.ParameterSetName -eq 'KB') { $kbResults[0] } else { $idx = [int](Read-Host "Select application number (1-$($kbResults.Count))") $kbResults[$idx - 1] } Write-Host "Selected: $($appChoice.applicationName)" -ForegroundColor Green # Get versions + sources for the chosen app Write-Host "`n=== Fetching sources ===" -ForegroundColor Magenta $sources = @(Search-JuribaAppRKnowledgeBase -ApplicationId $appChoice.applicationId) if (-not $sources.Count) { throw "No sources found for '$($appChoice.applicationName)'." } # Latest versions first $uniqueVersions = $sources | Select-Object -ExpandProperty version -Unique | Sort-Object { try { [version]($_ -replace '[^0-9.]', '') } catch { [version]'0.0' } } -Descending $topVersions = @($uniqueVersions | Select-Object -First 5) $verChoice = if ($topVersions.Count -eq 1 -or $PSCmdlet.ParameterSetName -eq 'KB') { $topVersions[0] } else { Write-Host "Latest versions:" for ($i = 0; $i -lt $topVersions.Count; $i++) { Write-Host (" [{0}] {1}" -f ($i + 1), $topVersions[$i]) } $idx = Read-Host "Select version (1-$($topVersions.Count)) [default: 1 = latest]" if (-not $idx) { $idx = 1 } $topVersions[[int]$idx - 1] } Write-Host "Version: $verChoice" -ForegroundColor Green # Filter sources for the chosen version $versionSources = @($sources | Where-Object { $_.version -eq $verChoice }) $srcChoice = if ($versionSources.Count -eq 1 -or $PSCmdlet.ParameterSetName -eq 'KB') { # Auto-select: prefer x64 EXE $x64Exe = $versionSources | Where-Object { $_.architecture -eq 64 -and $_.fileName -match '\.exe$' } | Select-Object -First 1 if ($x64Exe) { $x64Exe } else { $versionSources[0] } } else { Write-Host "Sources for v${verChoice}:" for ($i = 0; $i -lt $versionSources.Count; $i++) { $arch = switch ($versionSources[$i].architecture) { 32 { 'x86' } 64 { 'x64' } default { "arch$($versionSources[$i].architecture)" } } Write-Host (" [{0}] {1} ({2})" -f ($i + 1), $versionSources[$i].fileName, $arch) } $idx = Read-Host "Select source (1-$($versionSources.Count))" $versionSources[[int]$idx - 1] } Write-Host "Source: $($srcChoice.fileName)" -ForegroundColor Green # Download the installer locally for Send-JuribaAppRSetupFile Write-Host "`n=== Downloading $($srcChoice.fileName) ===" -ForegroundColor Magenta $downloadDir = Join-Path ([System.IO.Path]::GetTempPath()) "JuribaAppR-SelfService" if (-not (Test-Path $downloadDir)) { New-Item -ItemType Directory -Path $downloadDir | Out-Null } $localPath = Join-Path $downloadDir $srcChoice.fileName if (Test-Path $localPath) { Write-Host "Already cached: $localPath" -ForegroundColor DarkGray } else { Invoke-WebRequest -Uri $srcChoice.downloadUrl -OutFile $localPath -UseBasicParsing Write-Host ("Downloaded {0:N2} MB to {1}" -f ((Get-Item $localPath).Length / 1MB), $localPath) -ForegroundColor Green $cleanupTempDownload = $true } # `??` only fires on $null. Some KB entries return an empty string for # productName/vendor — use a truthy check so "" falls back to the # application-level name/vendor instead of submitting a blank field. $metaHint = @{ Name = if ($srcChoice.productName) { $srcChoice.productName } else { $appChoice.applicationName } Manufacturer = if ($srcChoice.vendor) { $srcChoice.vendor } else { $appChoice.vendorName } Version = $srcChoice.version } } # --- Local source -------------------------------------------------------- else { if (-not $SetupFilePath) { $SetupFilePath = Read-Host "Path to installer" } if (-not (Test-Path $SetupFilePath -PathType Leaf)) { throw "File not found: $SetupFilePath" } $localPath = (Resolve-Path $SetupFilePath).Path } # --- Upload -------------------------------------------------------------- Write-Host "`n=== Uploading ===" -ForegroundColor Magenta $upload = Send-JuribaAppRSetupFile -FilePath $localPath -Verbose:$VerbosePreference Write-Host ("Uploaded: {0} ({1:N2} MB, {2} chunks, UUID={3})" -f $upload.FileName, ($upload.FileSize / 1MB), $upload.TotalChunks, $upload.Uuid) -ForegroundColor Green # --- Create application -------------------------------------------------- Write-Host "`n=== Creating application ===" -ForegroundColor Magenta $splat = @{ Uuid = $upload.Uuid FileName = $upload.FileName FileSize = $upload.FileSize TotalChunks = $upload.TotalChunks RunImmediately = $true } # Metadata priority: KB hint > PE info on disk > server extraction (handled by cmdlet) if ($metaHint) { if ($metaHint.Name) { $splat['Name'] = $metaHint.Name } if ($metaHint.Manufacturer) { $splat['Manufacturer'] = $metaHint.Manufacturer } if ($metaHint.Version) { $splat['Version'] = $metaHint.Version } } else { if ($upload.Name) { $splat['Name'] = $upload.Name } if ($upload.Manufacturer) { $splat['Manufacturer'] = $upload.Manufacturer } if ($upload.Version) { $splat['Version'] = $upload.Version } } New-JuribaAppRApplication @splat -Verbose:$VerbosePreference | Out-Null Write-Host "Application submitted" -ForegroundColor Green # --- Wait for application ID --------------------------------------------- Write-Host "`n=== Waiting for application ID ===" -ForegroundColor Magenta $appId = $null $creationDeadline = (Get-Date).AddMinutes(5) while ((Get-Date) -lt $creationDeadline) { try { $state = Get-JuribaAppRApplicationCreationState -UploadId $upload.Uuid $resolved = if ($state.data.applicationId) { $state.data.applicationId } elseif ($state.applicationId) { $state.applicationId } else { $null } if ($resolved -and $resolved -gt 0) { $appId = $resolved; break } } catch { Write-Verbose "Creation-state poll failed: $($_.Exception.Message)" } Write-Host " waiting..." -ForegroundColor DarkGray Start-Sleep -Seconds 10 } if (-not $appId) { throw "Timed out waiting for the application ID. Check the UI at $activeInstance." } Write-Host "Application ID: $appId" -ForegroundColor Green Write-Host ("View in App Readiness: {0}/applications/fullStatus/{1}" -f $activeInstance, $appId) -ForegroundColor Cyan # --- Watch packaging ----------------------------------------------------- Write-Host "`n=== Watching packaging status ===" -ForegroundColor Magenta $result = Watch-JuribaAppRApplicationStatus -AppId $appId ` -IntervalSeconds $IntervalSeconds -TimeoutMinutes $TimeoutMinutes # --- Result summary ------------------------------------------------------ Write-Host "`n=== Result ===" -ForegroundColor Magenta $statusColor = if ($result.Status -match 'Fail|Cancel|Timeout') { 'Red' } else { 'Green' } Write-Host ("Status: {0}" -f $result.Status) -ForegroundColor $statusColor Write-Host ("Progress: {0}%" -f $result.Progress) Write-Host ("Elapsed: {0}" -f $result.Elapsed) Write-Host ("Polls: {0}" -f $result.PollCount) Write-Host ("URL: {0}/applications/fullStatus/{1}" -f $activeInstance, $appId) -ForegroundColor Cyan if ($result.Status -match 'Fail|Cancel') { Write-Host "`nEvent history:" -ForegroundColor Yellow try { $events = Get-JuribaAppRApplicationEvent -AppId $appId $events | ForEach-Object { $ts = $_.date ?? $_.timestamp ?? '' $msg = $_.message ?? $_.description ?? ($_ | ConvertTo-Json -Compress) Write-Host " [$ts] $msg" } } catch { Write-Host " (could not retrieve events: $($_.Exception.Message))" -ForegroundColor DarkGray } } # Return the result object for pipeline use $result } finally { if ($cleanupTempDownload -and $localPath -and (Test-Path $localPath)) { Remove-Item $localPath -Force -ErrorAction SilentlyContinue } if ($connectedHere) { Disconnect-JuribaAppR } } |