Public/get-guestaudit.ps1

# get-guestaudit.ps1
# Lists all guest accounts in the tenant with invite status and last sign-in.
# Useful for quarterly access reviews — guests that have never signed in
# or haven't been active in 90+ days are candidates for removal.
# Requires: Graph (User.Read.All, Directory.Read.All)

if (-not (Get-MgContext)) {
    Connect-MgGraph -Scopes "User.Read.All", "Directory.Read.All" -ContextScope Process
}

Write-Host "Fetching guest accounts..."
$guests = Get-MgUser -All -Filter "userType eq 'Guest'" `
    -Property "DisplayName,UserPrincipalName,CreatedDateTime,ExternalUserState,ExternalUserStateChangeDateTime"

if ($guests.Count -eq 0) {
    Write-Host "No guest accounts found."
    exit
}

$cutoff = (Get-Date).AddDays(-90)

$results = foreach ($g in $guests) {
    $notes = if ($g.ExternalUserState -eq "PendingAcceptance") {
        "Invite never accepted"
    } elseif ($g.CreatedDateTime -lt $cutoff) {
        "Active >90 days ago"
    } else {
        "Recent"
    }

    [PSCustomObject]@{
        "Display Name"   = $g.DisplayName
        "UPN"            = $g.UserPrincipalName
        "Invite Status"  = $g.ExternalUserState
        "Created"        = $g.CreatedDateTime
        "Notes"          = $notes
    }
}

Write-Host "`n$($guests.Count) guest account(s) found:`n"
$results | Sort-Object "Notes", "Created" | Format-Table -AutoSize

$path = "$env:USERPROFILE\Desktop\GuestAudit_$(Get-Date -Format 'yyyyMMdd').csv"
$results | Export-Csv -Path $path -NoTypeInformation
Write-Host "Exported to $path"