Examples/Test-WatchAndPublishIntune.ps1

<#
.SYNOPSIS
  Watch for applications with IntuneWin packages that have passed smoke testing
  and automatically publish them to Intune.
.DESCRIPTION
  Polls the application list on an interval, looking for apps that:
    1. Are in a publishable workflow state (ReadyForUat, ReadyForPublishing, or configurable)
    2. Have an IntuneWin package (packageType 4)
    3. Have completed smoke testing with a pass (green) or pass-with-warnings (amber) result

  When a qualifying app is found, it is published to Intune via the configured
  integration connector. Already-published apps are tracked so they are not
  published twice.

  Requires an Intune integration connector to be configured on the instance.
.PARAMETER InstanceUrl
  The base URL of the App Readiness instance.
.PARAMETER APIKey
  Your API key for authentication.
.PARAMETER TargetStatuses
  Workflow statuses to consider for publishing. Default: ReadyForUat, ReadyForPublishing.
.PARAMETER AllowAmber
  When specified, apps with amber (warnings) smoke test results are also published.
  By default, only green (clean pass) results qualify.
.PARAMETER IntervalSeconds
  Seconds between polling cycles. Default 300 (5 minutes).
.PARAMETER TimeoutMinutes
  Maximum minutes to run before exiting. Default 0 (run indefinitely).
.PARAMETER DryRun
  When specified, identifies qualifying apps but does not actually publish them.
.EXAMPLE
  .\Test-WatchAndPublishIntune.ps1 -InstanceUrl "https://demo.appr.juriba.app" `
      -APIKey "your-key" -DryRun
  Dry-run: shows which apps would be published without actually publishing.
.EXAMPLE
  .\Test-WatchAndPublishIntune.ps1 -InstanceUrl "https://demo.appr.juriba.app" `
      -APIKey "your-key" -AllowAmber -IntervalSeconds 60
  Watches every 60 seconds, publishing apps with green or amber test results.
#>


[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '',
    Justification = 'Interactive example script - user-facing colored console output for watcher progress and results.')]
[CmdletBinding()]
param (
    [Parameter(Mandatory = $true)]
    [string]$InstanceUrl,

    [Parameter(Mandatory = $true)]
    [string]$APIKey,

    [string[]]$TargetStatuses = @('Ready for UAT', 'ReadyForUat', 'Ready for Publishing', 'ReadyForPublishing'),

    [switch]$AllowAmber,

    [int]$IntervalSeconds = 300,

    [int]$TimeoutMinutes = 0,

    [switch]$DryRun
)

$ErrorActionPreference = 'Stop'

# Import the module
$modulePath = Join-Path (Join-Path $PSScriptRoot '..') 'Juriba.AppR.psd1'
Import-Module $modulePath -Force
Write-Host "Module imported" -ForegroundColor Cyan

# Connect
Write-Host "`n=== Connect ===" -ForegroundColor Magenta
Connect-JuribaAppR -Instance $InstanceUrl -APIKey $APIKey
Write-Host "Connected to $InstanceUrl"

# Track which apps we've already published to avoid duplicates
$publishedAppIds = @{}

# RAG status values: 1 = green (pass), 2 = amber (warnings), 3 = red (fail)
$acceptableRAG = @(1)
if ($AllowAmber) { $acceptableRAG += 2 }

$ragLabel = if ($AllowAmber) { 'green or amber' } else { 'green only' }
$modeLabel = if ($DryRun) { 'DRY RUN' } else { 'LIVE' }

Write-Host "`n=== Watching for IntuneWin apps ready to publish ===" -ForegroundColor Magenta
Write-Host " Mode: $modeLabel"
Write-Host " Target statuses: $($TargetStatuses -join ', ')"
Write-Host " Test threshold: $ragLabel"
Write-Host " Poll interval: $IntervalSeconds seconds"
if ($TimeoutMinutes -gt 0) {
    Write-Host " Timeout: $TimeoutMinutes minutes"
}
else {
    Write-Host " Timeout: none (runs until stopped)"
}

$startTime = Get-Date
$timeoutTime = if ($TimeoutMinutes -gt 0) { $startTime.AddMinutes($TimeoutMinutes) } else { [DateTime]::MaxValue }
$pollCount = 0

while ((Get-Date) -lt $timeoutTime) {
    $pollCount++
    $timestamp = (Get-Date).ToString('HH:mm:ss')
    Write-Host "`n[$timestamp] Poll #$pollCount" -ForegroundColor Cyan

    # Get all applications
    $allApps = Get-JuribaAppRApplicationList -AllUsers -Lite

    # Filter to target statuses
    $candidates = @($allApps | Where-Object {
        $_.status -and ($TargetStatuses -contains $_.status) -and
        $_.id -and (-not $publishedAppIds.ContainsKey($_.id))
    })

    if ($candidates.Count -eq 0) {
        Write-Host " No new candidates in target statuses." -ForegroundColor DarkGray
        Start-Sleep -Seconds $IntervalSeconds
        continue
    }

    Write-Host " Checking $($candidates.Count) candidate app(s)..."

    foreach ($app in $candidates) {
        $appLabel = "$($app.name) v$($app.version) ($($app.manufacturer)) [id=$($app.id)]"

        # Get smoke test results
        try {
            $testResults = Get-JuribaAppRTestResult -AppId $app.id
        }
        catch {
            Write-Host " $appLabel - Could not get test results: $($_.Exception.Message)" -ForegroundColor DarkGray
            continue
        }

        # Find IntuneWin test results (typeOfPackage = 4)
        $intuneTests = @($testResults | Where-Object { $_.typeOfPackage -eq 4 })
        if ($intuneTests.Count -eq 0) {
            Write-Host " $appLabel - No IntuneWin package" -ForegroundColor DarkGray
            continue
        }

        # Check if any IntuneWin smoke test is complete and passed
        $passedTest = $null
        foreach ($test in $intuneTests) {
            foreach ($eg in $test.evergreenInformation) {
                if ($eg.complete -and $acceptableRAG -contains $eg.rAGStatus) {
                    $passedTest = $eg
                    break
                }
            }
            if ($passedTest) { break }
        }

        if (-not $passedTest) {
            # Check if there's a completed but failed test
            $failedTest = $intuneTests.evergreenInformation | Where-Object { $_.complete } | Select-Object -First 1
            if ($failedTest) {
                $ragText = switch ($failedTest.rAGStatus) { 1 { 'GREEN' } 2 { 'AMBER' } 3 { 'RED' } default { "RAG=$($failedTest.rAGStatus)" } }
                Write-Host " $appLabel - IntuneWin test completed but $ragText (skipping)" -ForegroundColor Yellow
            }
            else {
                Write-Host " $appLabel - IntuneWin test not yet complete" -ForegroundColor DarkGray
            }
            continue
        }

        $ragText = switch ($passedTest.rAGStatus) { 1 { 'GREEN' } 2 { 'AMBER' } }
        Write-Host " $appLabel - IntuneWin test PASSED ($ragText)" -ForegroundColor Green

        if ($DryRun) {
            Write-Host " [DRY RUN] Would publish to Intune" -ForegroundColor Yellow
            $publishedAppIds[$app.id] = $true
            continue
        }

        # Publish to Intune
        Write-Host " Publishing to Intune..." -ForegroundColor Cyan
        try {
            $publishBody = @{
                applicationId = $app.id
                packageType   = 4   # IntuneWin
            }
            $result = Invoke-JuribaAppRPublishIntune -Body $publishBody
            Write-Host " Published successfully." -ForegroundColor Green
            if ($result) {
                Write-Host " Response: $($result | ConvertTo-Json -Compress)"
            }
            $publishedAppIds[$app.id] = $true
        }
        catch {
            Write-Host " Publish FAILED: $($_.Exception.Message)" -ForegroundColor Red
        }
    }

    # Summary
    if ($publishedAppIds.Count -gt 0) {
        Write-Host "`n Total published so far: $($publishedAppIds.Count)" -ForegroundColor Green
    }

    Start-Sleep -Seconds $IntervalSeconds
}

if ($TimeoutMinutes -gt 0) {
    Write-Host "`nTimeout reached after $TimeoutMinutes minutes ($pollCount polls)." -ForegroundColor Yellow
}

Write-Host "`n=== Summary ===" -ForegroundColor Magenta
Write-Host "Total apps published: $($publishedAppIds.Count)"
Write-Host "Polls completed: $pollCount"

# Disconnect
Disconnect-JuribaAppR