<# .SYNOPSIS Exports Microsoft 365 user recertification and licensing report using Microsoft Graph SDK. .DESCRIPTION Retrieves all M365 users, resolves license SKU IDs to readable part numbers, extracts last interactive sign-in dates, and exports the dataset to CSV. .EXAMPLE .\Export-M365UserCertification.ps1 -OutputPath ".\M365-User-Certification-Export.csv" #> [CmdletBinding()] param( [Parameter(Mandatory = $false)] [string]$OutputPath = ".\M365-User-Certification-Export.csv" ) # Silence Graph cmdlet progress bars to drastically decrease execution time $OriginalProgressPreference = $ProgressPreference $ProgressPreference = 'SilentlyContinue' try { # Check existing Microsoft Graph session $CurrentContext = Get-MgContext $RequiredScopes = @("User.Read.All", "Directory.Read.All", "AuditLog.Read.All") if (-not $CurrentContext) { Write-Host "Connecting to Microsoft Graph..." -ForegroundColor Cyan Connect-MgGraph -Scopes $RequiredScopes } else { Write-Host "Connected as $($CurrentContext.Account)" -ForegroundColor Green } # Resolve relative path to absolute path $ResolvedOutputPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath) Write-Host "Building license SKU lookup table..." -ForegroundColor Cyan $SkuLookup = @{} Get-MgSubscribedSku -All | ForEach-Object { $SkuLookup[$_.SkuId.ToString()] = $_.SkuPartNumber } # Explicitly define target properties (signInActivity requires AuditLog.Read.All) $Properties = @( "id", "displayName", "userPrincipalName", "userType", "accountEnabled", "assignedLicenses", "externalUserState", "signInActivity" ) Write-Host "Fetching user data from Microsoft Graph..." -ForegroundColor Cyan $Users = Get-MgUser -All -Property $Properties Write-Host "Processing $($Users.Count) users..." -ForegroundColor Cyan $Export = foreach ($User in $Users) { # Resolve licenses $AssignedLicenses = if ($User.AssignedLicenses -and $User.AssignedLicenses.Count -gt 0) { ($User.AssignedLicenses | ForEach-Object { $SkuId = $_.SkuId.ToString() if ($SkuLookup.ContainsKey($SkuId)) { $SkuLookup[$SkuId] } else { $SkuId } }) -join "; " } else { "None" } # Safe null-check for SignInActivity $LastSignIn = if ($User.SignInActivity -and $User.SignInActivity.LastSignInDateTime) { $User.SignInActivity.LastSignInDateTime } else { "Never / No Record" } [PSCustomObject]@{ Name = $User.DisplayName UserPrincipalName = $User.UserPrincipalName UserType = $User.UserType AccountEnabled = $User.AccountEnabled AssignedLicenses = $AssignedLicenses GuestInvitationState = $User.ExternalUserState LastInteractiveSignInDate = $LastSignIn } } # Export results to CSV $Export | Sort-Object Name | Export-Csv -Path $ResolvedOutputPath -NoTypeInformation -Encoding UTF8 Write-Host "Export complete: $ResolvedOutputPath" -ForegroundColor Green } catch { Write-Error "An error occurred during execution: $_" } finally { # Restore original progress preference $ProgressPreference = $OriginalProgressPreference }