Forum Discussion
Trying to fetch mail info using Microsoft Graph
# Replace these with your app registration details
$tenantId = ""
$clientSecret = ""
$clientId = ""
# OAuth 2.0 token endpoint for your tenant
$tokenUrl = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
# Microsoft Graph API base URL
$graphApiUrl = "https://graph.microsoft.com/v1.0"
# The resource scope we are requesting (Mail.Read, MailboxSettings.Read, User.ReadBasic.All)
$scope = "https://graph.microsoft.com/.default"
# Request an OAuth 2.0 token from Azure AD using the client credentials flow
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenUrl -ContentType "application/x-www-form-urlencoded" -Body @{
client_id = $clientId
client_secret = $clientSecret
scope = $scope
grant_type = "client_credentials"
}
# Extract the access token from the response
$accessToken = $tokenResponse.access_token
# Headers for authenticated requests to Microsoft Graph
$headers = @{
Authorization = "Bearer $accessToken"
}
# Function to fetch paginated results from Microsoft Graph API
function Get-PaginatedData {
param (
[Parameter(Mandatory = $true)]
[string]$initialUrl
)
$results = @()
$nextLink = $initialUrl
while ($nextLink) {
try {
$response = Invoke-RestMethod -Uri $nextLink -Headers $headers
$results += $response.value
# Check if there is more data to fetch
if ($response.'@odata.nextLink') {
$nextLink = $response.'@odata.nextLink'
} else {
$nextLink = $null
}
} catch {
# Capture and display detailed error information
Write-Host "Error fetching data:"
if ($_.Exception.Response -is [System.Net.HttpWebResponse]) {
$httpResponse = $_.Exception.Response
Write-Host "StatusCode: $($httpResponse.StatusCode)"
Write-Host "StatusDescription: $($httpResponse.StatusDescription)"
# Read the response stream for detailed error
$streamReader = New-Object System.IO.StreamReader($httpResponse.GetResponseStream())
$responseBody = $streamReader.ReadToEnd()
Write-Host "Response Body: $responseBody"
} else {
Write-Host "An unknown error occurred."
}
break
}
}
return $results
}
# Function to get all emails for a user
function Get-EmailsForUser {
param (
[Parameter(Mandatory = $true)]
[string]$userPrincipalName
)
$mailApiUrl = "$graphApiUrl/users/$userPrincipalName/messages"
return Get-PaginatedData -initialUrl $mailApiUrl
}
# Function to get attachment details for a specific email
function Get-AttachmentsForEmail {
param (
[Parameter(Mandatory = $true)]
[string]$userPrincipalName,
[Parameter(Mandatory = $true)]
[string]$emailId
)
$attachmentApiUrl = "$graphApiUrl/users/$userPrincipalName/messages/$emailId/attachments"
return Get-PaginatedData -initialUrl $attachmentApiUrl
}
# Function to get mailbox settings for a user (including last access time)
function Get-MailboxSettings {
param (
[Parameter(Mandatory = $true)]
[string]$userPrincipalName
)
$mailboxSettingsApiUrl = "$graphApiUrl/users/$userPrincipalName/mailboxSettings"
$mailboxSettings = Invoke-RestMethod -Uri $mailboxSettingsApiUrl -Headers $headers
return $mailboxSettings
}
# Step 1: Fetch only user mailboxes by filtering on userType eq 'Member'
$usersApiUrl = "$graphApiUrl/users?`$filter=userType eq 'Member'"
Write-Host "Fetching user mailboxes..."
$users = Get-PaginatedData -initialUrl $usersApiUrl
if ($users.Count -eq 0) {
Write-Host "No user mailboxes found. Aborting script."
exit
}
# Initialize result collection
$mailboxDataCollection = @()
# Step 2: Loop through each user and gather mailbox data
foreach ($user in $users) {
$userPrincipalName = $user.userPrincipalName
Write-Host "Processing mailbox for $userPrincipalName..."
# Initialize user data
$mailData = @{
User = $userPrincipalName
TotalEmails = 0
TotalAttachments = 0
AttachmentsTypeCount = @{ 'PDF' = 0; 'Word' = 0; 'Excel' = 0; 'PPT' = 0; 'Image' = 0; 'Other' = 0 }
LastEmailReceived = $null
LastAccessTime = $null
}
# Get emails for this user
$emails = Get-EmailsForUser -userPrincipalName $userPrincipalName
foreach ($email in $emails) {
$mailData.TotalEmails++
# Track the last email received time
if (-not $mailData.LastEmailReceived -or $mailData.LastEmailReceived -lt $email.receivedDateTime) {
$mailData.LastEmailReceived = $email.receivedDateTime
}
# Check for attachments
if ($email.hasAttachments) {
$attachments = Get-AttachmentsForEmail -userPrincipalName $userPrincipalName -emailId $email.id
foreach ($attachment in $attachments) {
$mailData.TotalAttachments++
# Determine the type of attachment by file extension
if ($attachment.name -match '\.pdf$') { $mailData.AttachmentsTypeCount['PDF']++ }
elseif ($attachment.name -match '\.docx?$') { $mailData.AttachmentsTypeCount['Word']++ }
elseif ($attachment.name -match '\.xlsx?$') { $mailData.AttachmentsTypeCount['Excel']++ }
elseif ($attachment.name -match '\.pptx?$') { $mailData.AttachmentsTypeCount['PPT']++ }
elseif ($attachment.contentType -match 'image/') { $mailData.AttachmentsTypeCount['Image']++ }
else { $mailData.AttachmentsTypeCount['Other']++ }
}
}
}
# Get mailbox settings (last access time)
$mailboxSettings = Get-MailboxSettings -userPrincipalName $userPrincipalName
$mailData.LastAccessTime = $mailboxSettings.lastSignInDateTime
# Add user data to the results
$mailboxDataCollection += $mailData
}
# Step 3: Output results
$mailboxDataCollection | Format-Table -AutoSize
# Optionally, export the results to CSV
$mailboxDataCollection | Export-Csv -Path "MailboxDataReport.csv" -NoTypeInformation
My goal is to achieve:
- Total Numbers of email by each mailbox
- Total Attachments per email by each mailbox
- Type of attachments (PDF, Word, Excel, PPT, Image, Etc..)
- Last access
Can someone help me in guiding where i am going wrong i have created azure app with proper permissions but my script is not giving me output as expected.
2 Replies
# Replace these with your app registration details
$tenantId = "<YourTenantId>"
$clientSecret = "<YourClientSecret>"
$clientId = "<YourClientId>"# OAuth 2.0 token endpoint for your tenant
$tokenUrl = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"# Microsoft Graph API base URL
$graphApiUrl = "https://graph.microsoft.com/v1.0"# The resource scope we are requesting (Mail.Read, MailboxSettings.Read, User.ReadBasic.All)
$scope = "https://graph.microsoft.com/.default"# Request an OAuth 2.0 token from Azure AD using the client credentials flow
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenUrl -ContentType "application/x-www-form-urlencoded" -Body @{
client_id = $clientId
client_secret = $clientSecret
scope = $scope
grant_type = "client_credentials"
}# Extract the access token from the response
$accessToken = $tokenResponse.access_token# Headers for authenticated requests to Microsoft Graph
$headers = @{
Authorization = "Bearer $accessToken"
}# Function to fetch paginated results from Microsoft Graph API
function Get-PaginatedData {
param (
[Parameter(Mandatory = $true)]
[string]$initialUrl
)$results = @()
$nextLink = $initialUrlwhile ($nextLink) {
try {
$response = Invoke-RestMethod -Uri $nextLink -Headers $headers
Write-Host "API Response: $($response | ConvertTo-Json -Depth 3)" # Debugging the response
$results += $response.value
# Check if there is more data to fetch
if ($response.'@odata.nextLink') {
$nextLink = $response.'@odata.nextLink'
} else {
$nextLink = $null
}
} catch {
Write-Host "Error fetching data from: $nextLink"
Write-Host "Error Message: $_"
break
}
}
return $results
}# Function to get all emails for a user
function Get-EmailsForUser {
param (
[Parameter(Mandatory = $true)]
[string]$userPrincipalName
)$mailApiUrl = "$graphApiUrl/users/$userPrincipalName/messages"
return Get-PaginatedData -initialUrl $mailApiUrl
}# Function to get attachment details for a specific email
function Get-AttachmentsForEmail {
param (
[Parameter(Mandatory = $true)]
[string]$userPrincipalName,
[Parameter(Mandatory = $true)]
[string]$emailId
)$attachmentApiUrl = "$graphApiUrl/users/$userPrincipalName/messages/$emailId/attachments"
return Get-PaginatedData -initialUrl $attachmentApiUrl
}# Function to get mailbox settings for a user (including last access time)
function Get-MailboxSettings {
param (
[Parameter(Mandatory = $true)]
[string]$userPrincipalName
)$mailboxSettingsApiUrl = "$graphApiUrl/users/$userPrincipalName/mailboxSettings"
try {
$mailboxSettings = Invoke-RestMethod -Uri $mailboxSettingsApiUrl -Headers $headers
return $mailboxSettings
} catch {
Write-Host "Error fetching mailbox settings for $userPrincipalName"
Write-Host "Error Message: $_"
return $null
}
}# Step 1: Fetch only user mailboxes by filtering on userType eq 'Member'
$usersApiUrl = "$graphApiUrl/users?`$filter=userType eq 'Member'"
Write-Host "Fetching user mailboxes..."
$users = Get-PaginatedData -initialUrl $usersApiUrlif ($users.Count -eq 0) {
Write-Host "No user mailboxes found. Aborting script."
exit
}# Initialize result collection
$mailboxDataCollection = @()# Step 2: Loop through each user and gather mailbox data
foreach ($user in $users) {
$userPrincipalName = $user.userPrincipalNameWrite-Host "Processing mailbox for $userPrincipalName..."
# Initialize user data
$mailData = @{
User = $userPrincipalName
TotalEmails = 0
TotalAttachments = 0
AttachmentsTypeCount = @{ 'PDF' = 0; 'Word' = 0; 'Excel' = 0; 'PPT' = 0; 'Image' = 0; 'Other' = 0 }
LastEmailReceived = $null
LastAccessTime = $null
}# Get emails for this user
$emails = Get-EmailsForUser -userPrincipalName $userPrincipalName
foreach ($email in $emails) {
$mailData.TotalEmails++# Track the last email received time
if (-not $mailData.LastEmPossible Issues:
- API Permissions: Ensure that your Azure app has the appropriate Microsoft Graph API permissions, specifically:
- Mail.Read
- MailboxSettings.Read
- User.ReadBasic.All
- User.Read.All (if necessary) Also, make sure these permissions are granted with Admin consent for the tenant.
- API Query Limits: Microsoft Graph uses pagination for API calls when there is more data than can be returned in a single request. Your Get-PaginatedData function seems to handle pagination well, but it's important to check if the number of items exceeds the limit (which can vary depending on the endpoint).
- Handling Attachments: You're checking for attachments in emails, but attachment fetching might fail due to various reasons (e.g., large attachments or quota limits). Ensure that you are handling paginated attachment responses as well.
- Error Handling: There could be situations where the Graph API returns an error (e.g., permission issues, bad requests, etc.). You are handling errors inside the Get-PaginatedData function, but it's important to ensure that the actual response is valid before continuing.
Let's address these points and refine your script.
- API Permissions: Ensure that your Azure app has the appropriate Microsoft Graph API permissions, specifically: