Public/Update-TCMBaseline.ps1

function Update-TCMBaseline {
    <#
    .SYNOPSIS
        After approved changes, take a fresh snapshot and update the monitor baseline.
    .DESCRIPTION
        When you intentionally change your tenant configuration (e.g., adding a CA
        policy, updating authentication methods), those changes show as drift until
        you update the baseline.

        This cmdlet automates the rebaseline workflow:
        1. Takes a fresh snapshot of the current tenant
        2. Converts it to a baseline using the same profile
        3. Updates the monitor with the new baseline

        ⚠ WARNING: Updating the baseline deletes ALL existing drift records for the
        monitor. Only run this after confirming all current drift is intentional.

        When to run this:
        • After deploying approved policy changes
        • After Watch-TCMDrift confirms only expected drift
        • After onboarding new services that add resources

        When NOT to run this:
        • If you see unexpected drift — investigate first!
        • Before reviewing current drift with Watch-TCMDrift
    .PARAMETER MonitorId
        The monitor to update. If omitted, updates the first active monitor.
    .PARAMETER Profile
        Monitoring profile for the new baseline. Should match the original.
        Default: Recommended
    .PARAMETER Force
        Skip the confirmation prompt.
    .EXAMPLE
        # Review drift, then rebaseline
        Watch-TCMDrift
        Update-TCMBaseline

    .EXAMPLE
        # Rebaseline a specific monitor with SecurityCritical profile
        Update-TCMBaseline -MonitorId 'eca21d95-...' -Profile SecurityCritical

    .EXAMPLE
        # Non-interactive (scripts/automation)
        Update-TCMBaseline -Force
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param(
        [string]$MonitorId,

        [ValidateSet('SecurityCritical', 'Recommended', 'Full')]
        [string]$Profile = 'Recommended',

        [switch]$Force
    )

    Write-Host ''
    Write-Host '🔄 Update-TCMBaseline — Rebaseline after approved changes' -ForegroundColor Cyan
    Write-Host ''

    # ── Resolve monitor ─────────────────────────────────────────────
    Write-Host 'Retrieving current monitor...' -ForegroundColor White
    $monitor = if ($MonitorId) {
        Get-TCMMonitor -Id $MonitorId
    }
    else {
        $all = Get-TCMMonitor
        if ($all -is [array]) { $all[0] } else { $all }
    }

    if (-not $monitor) {
        Write-Error 'No monitor found. Create one first with Start-TCMMonitoring.'
        return
    }

    $mId = if ($monitor -is [System.Collections.IDictionary]) { $monitor['id'] } else { $monitor.id }
    $mName = if ($monitor -is [System.Collections.IDictionary]) { $monitor['displayName'] } else { $monitor.displayName }

    Write-Host " Monitor: $mName" -ForegroundColor DarkGray
    Write-Host " Profile: $Profile" -ForegroundColor DarkGray

    # ── Show current drift before rebaselining ──────────────────────
    $drifts = @(Get-TCMDrift -MonitorId $mId -Status 'active' 2>$null)
    if ($drifts.Count -gt 0) {
        Write-Host ''
        Write-Host " ⚠️ $($drifts.Count) active drift(s) that will be cleared:" -ForegroundColor Yellow
        foreach ($d in $drifts | Select-Object -First 5) {
            $shortType = ($d.ResourceType -split '\.')[-1]
            Write-Host " • $shortType — $($d.ResourceDisplay) ($($d.DriftedPropertyCount) changes)" -ForegroundColor Yellow
        }
        if ($drifts.Count -gt 5) {
            Write-Host " ... and $($drifts.Count - 5) more" -ForegroundColor Yellow
        }
    }
    else {
        Write-Host ' ✅ No active drift — baseline is already current.' -ForegroundColor Green
    }

    # ── Confirmation ────────────────────────────────────────────────
    if (-not $Force) {
        if (-not $PSCmdlet.ShouldProcess("Monitor '$mName'", 'Update baseline (deletes all existing drift records)')) {
            return
        }
    }

    # ── Take fresh snapshot ─────────────────────────────────────────
    Write-Host ''
    Write-Host 'Taking fresh snapshot...' -ForegroundColor White
    $snapshotName = "Rebaseline $(Get-Date -Format 'yyyyMMdd HHmmss')"
    $snapshot = New-TCMSnapshot -DisplayName $snapshotName -Wait

    $snapshotStatus = if ($snapshot -is [System.Collections.IDictionary]) { $snapshot['status'] } else { $snapshot.status }
    $snapshotId = if ($snapshot -is [System.Collections.IDictionary]) { $snapshot['id'] } else { $snapshot.id }

    if ($snapshotStatus -notin @('succeeded', 'succeededWithWarnings', 'partiallySuccessful')) {
        Write-Error "Snapshot failed with status: $snapshotStatus"
        return
    }

    # ── Convert to baseline ─────────────────────────────────────────
    Write-Host 'Converting snapshot to baseline...' -ForegroundColor White
    $snapshotContent = Get-TCMSnapshot -Id $snapshotId -IncludeContent
    $baseline = ConvertTo-TCMBaseline -SnapshotContent $snapshotContent -DisplayName $mName -Profile $Profile

    # ── Update the monitor ──────────────────────────────────────────
    Write-Host 'Updating monitor baseline...' -ForegroundColor White
    Update-TCMMonitor -Id $mId -Baseline $baseline -Confirm:$false

    # ── Clean up snapshot ───────────────────────────────────────────
    Remove-TCMSnapshot -Id $snapshotId -Confirm:$false 2>$null

    # ── Invalidate comparison cache ─────────────────────────────────
    if (Test-Path $script:CompareBaselineCachePath) {
        Remove-Item $script:CompareBaselineCachePath -Force 2>$null
    }

    # ── Done ────────────────────────────────────────────────────────
    Write-Host ''
    Write-Host '✅ Baseline updated successfully!' -ForegroundColor Green
    Write-Host " $($baseline.Resources.Count) resources now monitored with '$Profile' profile." -ForegroundColor White
    Write-Host ' All previous drift records have been cleared.' -ForegroundColor DarkGray
    Write-Host ''
    Write-Host ' Next: Watch-TCMDrift to verify clean state.' -ForegroundColor DarkGray
    Write-Host ''
}