Public/Watch-TCMDrift.ps1

function Watch-TCMDrift {
    <#
    .SYNOPSIS
        Single command to check your tenant for drift — console, HTML report, or Maester.
    .DESCRIPTION
        The daily command for TCM monitoring. Checks all monitors for active drift
        and presents results in the format you choose:

        • No switch: Console summary (quick check)
        • -Report: HTML report with admin portal links
        • -Maester: Sync to Maester + run drift tests
        • -CompareBaseline: Also detect new/deleted resources (uses quota)

    .PARAMETER Report
        Generate an HTML drift report with admin portal deep links.
    .PARAMETER Maester
        Sync drift to Maester format and run Invoke-Maester on the drift folder.
    .PARAMETER CompareBaseline
        Also detect new/deleted resources not tracked by the monitor.
        Uses a snapshot (quota impact) but results are cached for 1 hour.
    .PARAMETER MonitorId
        Check a specific monitor. If omitted, checks all monitors.
    .PARAMETER PassThru
        Return the drift objects for pipeline processing.
    .EXAMPLE
        # Quick console check
        Watch-TCMDrift

    .EXAMPLE
        # Full HTML report
        Watch-TCMDrift -Report

    .EXAMPLE
        # Maester integration with baseline comparison
        Watch-TCMDrift -Maester -CompareBaseline

    .EXAMPLE
        # Pipeline: get drifted resource details
        Watch-TCMDrift -PassThru | Where-Object { $_.DriftedPropertyCount -gt 0 }
    #>

    [CmdletBinding()]
    param(
        [switch]$Report,
        [switch]$Maester,
        [switch]$CompareBaseline,
        [string]$MonitorId,
        [switch]$PassThru
    )

    # ── Resolve mode ────────────────────────────────────────────────
    if ($Report) {
        # HTML report mode
        $reportParams = @{}
        if ($MonitorId) { $reportParams.MonitorId = $MonitorId }
        if ($CompareBaseline) { $reportParams.CompareBaseline = $true }
        $result = Export-TCMDriftReport @reportParams
        if ($PassThru) { return $result }
        return
    }

    if ($Maester) {
        # Maester sync + run mode
        $syncParams = @{}
        if ($MonitorId) { $syncParams.MonitorId = $MonitorId }
        if ($CompareBaseline) { $syncParams.CompareBaseline = $true }
        Sync-TCMDriftToMaester @syncParams

        # Resolve drift folder
        $driftPath = if ($env:MAESTER_TESTS_PATH) {
            Join-Path $env:MAESTER_TESTS_PATH 'Drift'
        } else {
            './tests/Maester/Drift'
        }
        if (Test-Path $driftPath) {
            Write-Host ''
            Invoke-Maester -Path $driftPath
        }
        return
    }

    # ── Console summary mode (default) ─────────────────────────────
    Write-Host ''
    Write-Host '🔍 Checking for configuration drift...' -ForegroundColor Cyan
    Write-Host ''

    $driftParams = @{ Status = 'active' }
    if ($MonitorId) { $driftParams.MonitorId = $MonitorId }
    $drifts = @(Get-TCMDrift @driftParams)

    $monitors = if ($MonitorId) {
        @(Get-TCMMonitor -Id $MonitorId)
    } else {
        @(Get-TCMMonitor)
    }

    if ($drifts.Count -eq 0) {
        Write-Host " ✅ No active drift across $($monitors.Count) monitor(s)." -ForegroundColor Green
        Write-Host " TCM checks every 6 hours automatically." -ForegroundColor DarkGray
    }
    else {
        Write-Host " ⚠️ $($drifts.Count) active drift(s) detected!" -ForegroundColor Yellow
        Write-Host ''

        # Group by resource type
        $grouped = $drifts | Group-Object -Property ResourceType
        foreach ($group in $grouped) {
            $shortType = ($group.Name -split '\.')[-1]
            Write-Host " $shortType ($($group.Count)):" -ForegroundColor White
            foreach ($d in $group.Group) {
                $propCount = $d.DriftedPropertyCount
                Write-Host " • $($d.ResourceDisplay) — $propCount changed propert$(if ($propCount -eq 1) {'y'} else {'ies'})" -ForegroundColor Yellow
                if ($d.DriftedProperties) {
                    foreach ($dp in $d.DriftedProperties | Select-Object -First 3) {
                        Write-Host " $($dp.propertyName): $($dp.baselineValue) → $($dp.currentValue)" -ForegroundColor DarkGray
                    }
                    if ($d.DriftedProperties.Count -gt 3) {
                        Write-Host " ... and $($d.DriftedProperties.Count - 3) more" -ForegroundColor DarkGray
                    }
                }
            }
            Write-Host ''
        }
    }

    # Optional: baseline comparison
    if ($CompareBaseline) {
        Write-Host ' 🔗 Checking for untracked resources...' -ForegroundColor Cyan
        $compareParams = @{}
        if ($MonitorId) { $compareParams.MonitorId = $MonitorId }
        $comparison = Compare-TCMBaseline @compareParams
        if ($comparison.HasDrift) {
            Write-Host " ⚠️ $($comparison.NewCount) new, $($comparison.DeletedCount) deleted resource(s) not in baseline." -ForegroundColor Yellow
            Write-Host " Run Update-TCMBaseline to adopt approved changes." -ForegroundColor DarkGray
        }
        else {
            Write-Host " ✅ All resources are tracked. Baseline is up to date." -ForegroundColor Green
        }
        Write-Host ''
    }

    # Hints
    if (-not $Report -and -not $Maester) {
        Write-Host ' Commands:' -ForegroundColor DarkGray
        Write-Host ' Watch-TCMDrift -Report # detailed HTML report' -ForegroundColor DarkGray
        Write-Host ' Watch-TCMDrift -Maester # Maester test results' -ForegroundColor DarkGray
        Write-Host ' Watch-TCMDrift -CompareBaseline # find untracked resources' -ForegroundColor DarkGray
        if ($drifts.Count -gt 0) {
            Write-Host ' Update-TCMBaseline # accept current state as new baseline' -ForegroundColor DarkGray
        }
        Write-Host ''
    }

    if ($PassThru) { $drifts }
}