Public/Plugins/Get-DataversePluginDrift.ps1

function Get-DataversePluginDrift {
    <#
    .SYNOPSIS
        Compares plugin configuration against Dataverse environment state.

    .DESCRIPTION
        Analyzes differences between the registrations.json configuration and actual
        Dataverse plugin registrations. Reports orphaned components, missing components,
        and configuration differences.

        This cmdlet wraps the ppds CLI tool.

    .PARAMETER ConfigPath
        Path to the registrations.json file to compare.

    .PARAMETER Profile
        Authentication profile name. If not specified, uses the active profile.

    .PARAMETER Environment
        Environment URL, friendly name, unique name, or ID.
        Overrides the profile's default environment if specified.

    .PARAMETER PassThru
        Return drift results as structured objects.

    .EXAMPLE
        Get-DataversePluginDrift -ConfigPath "./registrations.json"

        Shows drift for all assemblies using the active profile.

    .EXAMPLE
        Get-DataversePluginDrift -ConfigPath "./registrations.json" -Profile "dev" -PassThru

        Returns drift as objects for programmatic use.

    .OUTPUTS
        Text output by default. PSCustomObject with drift details if -PassThru is specified.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [Alias('RegistrationFile')]
        [string]$ConfigPath,

        [Parameter()]
        [string]$Profile,

        [Parameter()]
        [string]$Environment,

        [Parameter()]
        [switch]$PassThru
    )

    # Validate config file exists
    if (-not (Test-Path $ConfigPath)) {
        throw "Configuration file not found: $ConfigPath"
    }

    # Get the CLI tool
    $cliPath = Get-PpdsCli

    # Build arguments
    $cliArgs = @(
        'plugins', 'diff'
        '--config', (Resolve-Path $ConfigPath).Path
    )

    if ($Profile) {
        $cliArgs += '--profile'
        $cliArgs += $Profile
    }

    if ($Environment) {
        $cliArgs += '--environment'
        $cliArgs += $Environment
    }

    if ($PassThru) {
        $cliArgs += '--json'
    }

    Write-Verbose "Executing: $cliPath $($cliArgs -join ' ')"

    # Execute CLI and capture output
    $output = & $cliPath @cliArgs 2>&1

    # Check exit code (1 = drift detected, which is not an error for this cmdlet)
    if ($LASTEXITCODE -gt 1) {
        $errorMessage = $output | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }
        if ($errorMessage) {
            throw "Diff failed: $($errorMessage -join "`n")"
        }
        throw "Diff failed with exit code $LASTEXITCODE"
    }

    if ($PassThru) {
        # Parse JSON output
        $jsonOutput = $output | Where-Object { $_ -match '^\s*\[' } | Out-String
        try {
            return $jsonOutput | ConvertFrom-Json
        }
        catch {
            throw "Failed to parse diff output: $_"
        }
    }
    else {
        # Write CLI output to console
        $output | ForEach-Object {
            if ($_ -is [System.Management.Automation.ErrorRecord]) {
                Write-Warning $_.ToString()
            }
            elseif ($_ -match '^\s*[\{\[]') {
                # JSON output - skip in non-PassThru mode
            }
            else {
                Write-Verbose $_
            }
        }

        # Return whether drift was detected
        if ($LASTEXITCODE -eq 1) {
            Write-Warning "Drift detected. Run with -PassThru for details."
        }
    }
}