Public/Get-NSPADGpoCatalog.ps1

function Get-NSPADGpoCatalog {
    <#
    .SYNOPSIS
        Lists the GPO backups in a catalog folder (a share of exported, portable GPOs): one row per
        backup with its display name, backup ID, time, folder, and whether ADMX templates sit next
        to it. Zip files in the catalog are listed too (Kind = Zip) - Import-NSPADGpoBackup takes either.
    .DESCRIPTION
        Read-only. A backup is any {GUID} folder holding bkupInfo.xml (hidden, as Backup-GPO writes
        it), up to -Depth levels below -Path. Category is the first folder under -Path.
    .PARAMETER Path
        The catalog root, e.g. a share of GPO exports. Defaults to $env:NSP_GPO_CATALOG.
    .PARAMETER Depth
        How deep to look. Default 5.
    .EXAMPLE
        Get-NSPADGpoCatalog -Path '\\fileserver\GPO' | Format-Table Category, DisplayName, BackupTime
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [string]$Path = $env:NSP_GPO_CATALOG,
        [ValidateRange(1, 10)][int]$Depth = 5
    )
    if ([string]::IsNullOrWhiteSpace($Path)) { throw 'Give -Path (the GPO catalog folder), or set NSP_GPO_CATALOG.' }
    if (-not (Test-Path -LiteralPath $Path -PathType Container)) { throw "GPO catalog not found: $Path" }
    $root = (Get-Item -LiteralPath $Path).FullName.TrimEnd('\')
    $parsed = [datetime]::MinValue
    $category = { param($full) $rel = $full.Substring($root.Length).TrimStart('\'); if ($rel -match '\\') { $rel.Split('\')[0] } else { '' } }

    foreach ($info in @(Get-ChildItem -LiteralPath $root -Recurse -Depth $Depth -Force -File -Filter 'bkupInfo.xml' -ErrorAction SilentlyContinue)) {
        $b = Get-NSPADGpoBackupInfo -BackupFolder $info.DirectoryName
        if (-not $b) { continue }
        $hasAdmx = [bool](Get-ChildItem -LiteralPath (Split-Path -Parent $b.BackupRoot) -Recurse -Depth 2 -Filter '*.admx' -File -ErrorAction SilentlyContinue | Select-Object -First 1)
        [pscustomobject]@{
            PSTypeName  = 'NSP.ActiveDirectory.GpoCatalogEntry'
            Kind        = 'Backup'
            Category    = & $category $b.BackupFolder
            DisplayName = $b.DisplayName
            BackupId    = $b.BackupId
            BackupTime  = $(if ([datetime]::TryParse("$($b.BackupTime)", [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::None, [ref]$parsed)) { $parsed } else { $b.BackupTime })
            HasAdmx     = $hasAdmx
            Path        = $b.BackupRoot
        }
    }
    foreach ($zip in @(Get-ChildItem -LiteralPath $root -Recurse -Depth $Depth -File -Filter '*.zip' -ErrorAction SilentlyContinue)) {
        [pscustomobject]@{
            PSTypeName  = 'NSP.ActiveDirectory.GpoCatalogEntry'
            Kind        = 'Zip'
            Category    = & $category $zip.FullName
            DisplayName = $zip.BaseName
            BackupId    = $null
            BackupTime  = $zip.LastWriteTime
            HasAdmx     = $null
            Path        = $zip.FullName
        }
    }
}