🔧 New: User Management Graph PowerShell Toolkit

Simplify user tasks like bulk creation, updates, password resets, deletions, license checks & more — all from one place.

🚀 Launch Toolkit

Email Tenant Users Inventory Report with Graph PowerShell

For IT admins, keeping track of every user in the tenant is critical. From ensuring compliance to verifying license allocation and monitoring account health, a consolidated view of all user accounts saves hours of manual reporting. Instead of pulling the data from the portal, you can automate the process with Graph PowerShell. This script fetches all users in your tenant, compiles their essential details, generates a CSV report, and emails it to administrators or stakeholders.


i) Script


    # ===== Microsoft 365 Tenant Users -> CSV -> Email =====
    # Requires: Microsoft.Graph module
    # Scopes: User.Read.All, Directory.Read.All, Mail.Send
    
    # -------- Email & Output ----------
    $FromUser   = "admin@contoso.com"                      # Sender (must have mailbox)
    $ToList     = "it-ops@contoso.com;secops@contoso.com"  # Recipients (; or , separated)
    $Subject    = "Tenant Users Inventory Report"
    $CsvOutDir  = "$env:TEMP"
    
    # -------- Connect to Microsoft Graph ----------
    Import-Module Microsoft.Graph -ErrorAction Stop
    Connect-MgGraph -Scopes "User.Read.All","Directory.Read.All","Mail.Send"
    
    # -------- Get all users with important properties ----------
    $users = Get-MgUser -All -Property `
      "id,displayName,userPrincipalName,mail,accountEnabled,createdDateTime,department,jobTitle,usageLocation,userType,assignedLicenses,businessPhones,mobilePhone,lastPasswordChangeDateTime"
    
    # -------- Shape rows for CSV ----------
    $rows = $users | ForEach-Object {
      $primaryPhone = $null
      if ($_.BusinessPhones -and $_.BusinessPhones.Count -gt 0) { $primaryPhone = $_.BusinessPhones[0] }
      elseif ($_.MobilePhone) { $primaryPhone = $_.MobilePhone }
    
      [PSCustomObject]@{
        UserId                      = $_.Id
        DisplayName                 = $_.DisplayName
        UPN                         = $_.UserPrincipalName
        Mail                        = $_.Mail
        UserType                    = $_.UserType                 # Member / Guest
        AccountEnabled              = $_.AccountEnabled
        Department                  = $_.Department
        JobTitle                    = $_.JobTitle
        UsageLocation               = $_.UsageLocation
        LicenseCount                = ($_.AssignedLicenses | Measure-Object).Count
        PrimaryPhone                = $primaryPhone
        CreatedDate                 = $_.CreatedDateTime
        LastPasswordChangeDateTime  = $_.LastPasswordChangeDateTime
      }
    }
    
    # -------- Export to CSV ----------
    if (-not (Test-Path -Path $CsvOutDir)) { New-Item -ItemType Directory -Path $CsvOutDir | Out-Null }
    $ts = Get-Date -Format "yyyyMMdd_HHmmss"
    $csvPath = Join-Path $CsvOutDir ("Tenant_Users_Report_{0}.csv" -f $ts)
    $rows | Sort-Object DisplayName | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
    
    # -------- Prepare HTML Body ----------
    $totalUsers = ($rows | Measure-Object).Count
    $summaryHtml = @"
    
    

<html>
  <body style='font-family:Segoe UI,Arial,sans-serif'>
    <h3>Tenant Users Inventory Report</h3>
    <p>Total users: <b>$totalUsers</b></p>
    <p>The attached CSV includes: DisplayName, UPN, Mail, UserType, AccountEnabled, Department, JobTitle, UsageLocation, LicenseCount, Phone, CreatedDate, LastPasswordChange.</p>
  </body>
</html>

"@

# -------- Prepare Attachment ----------
$fileBytes     = [System.IO.File]::ReadAllBytes($csvPath)
$base64Content = [System.Convert]::ToBase64String($fileBytes)
$csvFileName   = [System.IO.Path]::GetFileName($csvPath)
$attachment = @{
  "@odata.type" = "#microsoft.graph.fileAttachment"
  name          = $csvFileName
  contentBytes  = $base64Content
  contentType   = "text/csv"
}

# -------- Build recipients array (split on ; or ,) ----------
$recipients = @()
$ToList.Split(@(';', ','), [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object {
  $addr = $_.Trim()
  if ($addr) { $recipients += @{ emailAddress = @{ address = $addr } } }
}

# -------- Prepare and Send Email ----------
$mail = @{
  message = @{
    subject = "$Subject"
    body    = @{
      contentType = "HTML"
      content     = $summaryHtml
    }
    toRecipients = $recipients
    attachments  = @($attachment)
  }
  saveToSentItems = $true
}

Send-MgUserMail -UserId $FromUser -BodyParameter $mail

Write-Host "Done. Users report saved at: $csvPath" -ForegroundColor Green
                            

ii) How the Script Works

  1. Connects to Microsoft Graph using the required scopes:
    • User.Read.All and Directory.Read.All to fetch all users.
    • Mail.Send to email the report.
  2. Retrieves all users with key properties such as DisplayName, UPN, Mail, UserType, Department, JobTitle, LicenseCount, CreatedDate, and LastPasswordChangeDateTime.
  3. Shapes the data into a clean table format, ensuring details like phone number and license count are simplified.
  4. Exports the report to CSV, timestamped for uniqueness.
  5. Generates an HTML summary for the email body with the total user count.
  6. Sends the email with the CSV attached to the administrators.

iii) Further Enhancements

  • Add sign-in activity (e.g., last login) by expanding Graph queries.
  • Include license SKU details instead of just license count.
  • Track MFA status for better security insights.
  • Integrate with Azure Automation or Task Scheduler for periodic reporting.
  • Forward data to a SIEM platform for advanced analysis.

iv) Use Cases

  • Compliance reporting: Provide auditors with evidence of user accounts and their statuses.
  • License monitoring: Quickly check how many users are licensed vs. unlicensed.
  • Security oversight: Detect stale accounts or accounts with old passwords.
  • Operations: Maintain up-to-date user inventory for HR and IT teams.

v) Possible Errors & Solutions

Error Cause Solution
Authorization_RequestDenied Insufficient permissions Use scopes User.Read.All, Directory.Read.All, Mail.Send with admin consent.
Get-MgUser not recognized Graph module missing/outdated Install or update Microsoft.Graph.
Empty CSV Filters or properties missing Verify Graph connection and ensure users exist in tenant.
Email not delivered $FromUser not mailbox-enabled Ensure sender account has a licensed mailbox.
Split error on recipients Wrong delimiter Separate multiple recipients with ; or ,.

vi) Conclusion

With this Graph PowerShell script, you can generate a comprehensive tenant user inventory report and automatically email it to stakeholders. It eliminates the need for manual exports, ensures administrators always have the latest data, and strengthens compliance, operations, and security posture. By scheduling it regularly, you can make user reporting hands-free and reliable.


Graph PowerShell Explorer Widget

20 Graph PowerShell cmdlets with easily accessible "working" examples.


Permission Required

Example:


                


                


                

© m365corner.com. All Rights Reserved. Design by HTML Codex