Forum Discussion

vrush96's avatar
vrush96
Copper Contributor
Sep 24, 2024

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:

  1. Total Numbers of email by each mailbox
  2. Total Attachments per email by each mailbox
  3. Type of attachments (PDF, Word, Excel, PPT, Image, Etc..)
  4. 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 = $initialUrl

        while ($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 $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.LastEm

  • Possible Issues:

    1. 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.
    2. 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).
    3. 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.
    4. 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.

Resources