Public/Export-EGIGroupSnapshot.ps1

function Export-EGIGroupSnapshot {
    <#
    .SYNOPSIS
        Exports all dynamic membership groups and their rules to a JSON snapshot file.

    .DESCRIPTION
        Intended to be run on a schedule (e.g. a daily Azure Automation runbook or
        scheduled task) and the output committed to a Git repository. Each commit
        then becomes a reviewable version of "what dynamic group rules looked like
        on this date" - the same policy-as-code pattern Microsoft recommends for
        Conditional Access, applied to dynamic groups.

    .PARAMETER Path
        File path to write the JSON snapshot to.

    .EXAMPLE
        Export-EGIGroupSnapshot -Path './snapshots/dynamic-groups-2026-07-21.json'
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string] $Path
    )

    $uri = "https://graph.microsoft.com/v1.0/groups?`$filter=groupTypes/any(c:c eq 'DynamicMembership')&`$select=id,displayName,membershipRule,membershipRuleProcessingState,isAssignableToRole"
    $groups = Invoke-EGIGraphPaged -Uri $uri

    $snapshot = [pscustomobject]@{
        CapturedAtUtc = (Get-Date).ToUniversalTime().ToString('o')
        GroupCount    = @($groups).Count
        Groups        = @($groups | ForEach-Object {
                [pscustomobject]@{
                    GroupId       = $_.id
                    DisplayName   = $_.displayName
                    MembershipRule = $_.membershipRule
                    ProcessingState = $_.membershipRuleProcessingState
                    RoleAssignable  = [bool]$_.isAssignableToRole
                }
            } | Sort-Object GroupId)
    }

    $snapshot | ConvertTo-Json -Depth 6 | Set-Content -Path $Path -Encoding utf8
    Write-Verbose "Wrote snapshot of $($snapshot.GroupCount) dynamic group(s) to $Path"
    return $snapshot
}