Private/Users.ps1
|
function Get-M365SnapshotUsers { param( [Parameter(Mandatory=$true)] [hashtable]$GraphHeaders, [Parameter(Mandatory=$true)] [int]$EffectiveMaxUsers, [Parameter(Mandatory=$true)] [switch]$LoadAllUsers, [Parameter(Mandatory=$true)] [int]$MaxUsers, [Parameter(Mandatory=$true)] [switch]$ReturnObjects ) $entraUsers = @() $userLastActivityByUpn = @{} try { $uri = "https://graph.microsoft.com/v1.0/users?`$select=displayName,userPrincipalName,accountEnabled,userType,assignedLicenses" $allUsers = @() do { $response = Invoke-RestMethod -Uri $uri ` -Headers $GraphHeaders ` -Method Get ` -ErrorAction Stop if ($response.value) { $remaining = $EffectiveMaxUsers - $allUsers.Count if ($remaining -gt 0) { $usersPage = @($response.value) if ($usersPage.Count -gt $remaining) { $allUsers += ($usersPage | Select-Object -First $remaining) } else { $allUsers += $usersPage } } } if ($allUsers.Count -ge $EffectiveMaxUsers) { $uri = $null } else { $uri = $response.'@odata.nextLink' } } while ($uri) try { $activeUsersUri = "https://graph.microsoft.com/v1.0/reports/getOffice365ActiveUserDetail(period='D30')" $activeUsersCsv = Invoke-RestMethod -Uri $activeUsersUri ` -Headers $GraphHeaders ` -Method Get ` -ErrorAction Stop if (-not [string]::IsNullOrWhiteSpace([string]$activeUsersCsv)) { $activeUserRows = $activeUsersCsv | ConvertFrom-Csv foreach ($row in @($activeUserRows)) { $upn = [string]$row.'User Principal Name' if ([string]::IsNullOrWhiteSpace($upn)) { continue } $lastActivityRaw = [string]$row.'Last Activity Date' $lastActivityDisplay = '(unknown)' if (-not [string]::IsNullOrWhiteSpace($lastActivityRaw)) { $parsedLastActivity = [datetime]::MinValue if ([datetime]::TryParse($lastActivityRaw, [ref]$parsedLastActivity)) { $lastActivityDisplay = $parsedLastActivity.ToString('yyyy-MM-dd') } else { $lastActivityDisplay = $lastActivityRaw } } $userLastActivityByUpn[$upn.ToLower()] = $lastActivityDisplay } } } catch { if (-not $ReturnObjects) { Write-Host "[INFO] User activity report unavailable (Reports.Read.All may be missing): $($_.Exception.Message)" -ForegroundColor DarkGray } } $entraUsers = $allUsers | Select-Object @{N='DisplayName';E={$_.displayName}}, @{N='UserPrincipalName';E={$_.userPrincipalName}}, @{N='UserType';E={$_.userType}}, @{N='AccountEnabled';E={$_.accountEnabled}}, @{N='HasLicenses';E={$_.assignedLicenses.Count -gt 0}}, @{N='LicenseCount';E={$_.assignedLicenses.Count}}, @{N='LastActivityDate';E={ $upn = [string]$_.userPrincipalName if ([string]::IsNullOrWhiteSpace($upn)) { '(unknown)' } else { $upnKey = $upn.ToLower() if ($userLastActivityByUpn.ContainsKey($upnKey)) { $userLastActivityByUpn[$upnKey] } else { '(unknown)' } } }} if (-not $ReturnObjects) { Write-Host "[OK] Found $($entraUsers.Count) users`n" -ForegroundColor Green if (-not $LoadAllUsers -and $entraUsers.Count -ge $MaxUsers) { Write-Host "[INFO] User collection limited to $MaxUsers items. Use -LoadAllUsers to remove this limit.`n" -ForegroundColor DarkGray } } } catch { if (-not $ReturnObjects) { Write-Host "[WARNING] Could not collect users: $($_.Exception.Message)`n" -ForegroundColor Yellow } } return [PSCustomObject]@{ Users = @($entraUsers) } } |