How to Search Emails with PowerShell

GURU

An excellent method to search for messages in Exchange Online mailboxes is with PowerShell. You can tweak the commands to your liking and get the results you need. Also, an export of the results to CSV file is great, so you can filter in there. In this article, you will learn how to search emails with PowerShell.

Install Microsoft Graph PowerShell

Run Windows PowerShell as administrator and Install Microsoft Graph PowerShell.

Install-Module Microsoft.Graph -Force
Install-Module Microsoft.Graph.Beta -AllowClobber -Force

Important: Always install the Microsoft Graph PowerShell and Microsoft Graph Beta PowerShell modules. That’s because some cmdlets are not yet available in the final version, and they will not work. Update both modules to the latest version before you run a cmdlet or script to prevent errors and incorrect results.

Configure app registration in Microsoft Entra

Before you can run the commands or scripts, you must first create an app registration with the correct permissions and a client secret to authenticate with Microsoft Graph PowerShell.

1. Register new application

Register a new application in Microsoft Entra ID:

  1. Sign in to Microsoft Entra admin center
  2. Click Identity > Application > App registrations
  3. Click New Registration
Search Emails with PowerShell new registration
  1. Name the application Search-Mail
  2. Select Accounts in this organizational directory only (EXOIP only – Single tenant)
  3. Click Register
Search Emails with PowerShell register applicationSearch Emails with PowerShell register application

The application Search-Mail has been successfully created. Copy the below values and paste them into Notepad, as you will need them later when connecting to Microsoft Graph PowerShell.

  1. Copy the Application (client) ID
  2. Copy the Directory (tenant) ID
Application overviewApplication overview

2. Assign API permissions

Assign API permissions for the application that you registered in the previous step:

  1. Click API permission
  2. Click Add a permision
Search Emails with PowerShell add permissionSearch Emails with PowerShell add permission
  1. Click Microsoft APIs
  2. Click Microsoft Graph
Search Emails with PowerShell Microsoft APIsSearch Emails with PowerShell Microsoft APIs
  1. Click Application permissions
  2. Search for User.Read.All
  3. Select Users > User.Read.All
Search Emails with PowerShell application permissions user readSearch Emails with PowerShell application permissions user read
  1. Search for Mail.Read
  2. Select Mail > Mail.Read
  3. Click Add permissions
Search Emails with PowerShell application permissions mail readSearch Emails with PowerShell application permissions mail read

The API permissions are added successfully. The next step is to grant admin consent.

You must grant admin consent for the permissions that you selected in the previous step:

  1. Click Grant admin consent for EXOIP
  2. Click Yes
Search Emails with PowerShell grant admin consentSearch Emails with PowerShell grant admin consent
  1. The green check mark shows that you granted admin consent successfully
Verify grant statusVerify grant status

4. Create Client Secret

After you register a new application in Microsoft Entra, assign API permissions, and grant admin consent, you need to create a client secret.

To create a Client Secret for your application in Microsoft Entra ID, follow these steps:

  1. Click Certificates & secrets
  2. Click Client secrets > New client secret
  3. Type Description
  4. Select an Expiration date
  5. Click Add

Note: The Client Secret expiration date has a maximum of 24 months (2 years). You can always Renew the Client Secret in Microsoft Entra ID with PowerShell.

Add Client SecretAdd Client Secret
  1. Copy the Client Secret value and save it in Notepad
Copy Client Secret valueCopy Client Secret value

Connect to Microsoft Graph PowerShell with Client Secret

You need to change the below parameters values to connect to MS Graph PowerShell with Client Secret:

  • Type the Application Client ID value in line 2
  • Type the Directory Tenant ID value in line 3
  • Type the Client Secret value value in line 4

Run the below PowerShell script.

# Configuration
$ClientId = "c6787ff5-08ec-4e96-b4d2-1d1782303310"
$TenantId = "eb403171-a4ec-4d98-a08f-1876318c9deb"
$ClientSecret = "6_q8Q~X9e-fjwWhVVP_WWx~ua4JMI.BWkI7Q7b7B"

# Convert the client secret to a secure string
$ClientSecretPass = ConvertTo-SecureString -String $ClientSecret -AsPlainText -Force

# Create a credential object using the client ID and secure string
$ClientSecretCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ClientId, $ClientSecretPass

# Connect to Microsoft Graph with Client Secret
Connect-MgGraph -TenantId $tenantId -ClientSecretCredential $ClientSecretCredential

Search emails on specific user

Get all messages from a specific user.

$User = "Amanda.Morgan@exoip.com"

Get-MgUserMessage -All -UserId "$User" |
Select-Object Subject, InternetMessageId, ReceivedDateTime,
@{Name = "Sender"; Expression = { $_.Sender.EmailAddress.Address } }, 
@{Name = "Recipients"; Expression = { $_.ToRecipients.EmailAddress.Address -join ', ' } } |
Out-GridView

Get all messages from a specific user that are read.

$User = "Amanda.Morgan@exoip.com"

Get-MgUserMessage -All -UserId "$User" -Filter "IsRead eq true" |
Select-Object Subject, InternetMessageId, ReceivedDateTime,
@{Name = "Sender"; Expression = { $_.Sender.EmailAddress.Address } }, 
@{Name = "Recipients"; Expression = { $_.ToRecipients.EmailAddress.Address -join ', ' } } |
Out-GridView

Get all messages from a specific user that are unread.

$User = "Amanda.Morgan@exoip.com"

Get-MgUserMessage -All -UserId "$User" -Filter "IsRead eq false" |
Select-Object Subject, InternetMessageId, ReceivedDateTime,
@{Name = "Sender"; Expression = { $_.Sender.EmailAddress.Address } }, 
@{Name = "Recipients"; Expression = { $_.ToRecipients.EmailAddress.Address -join ', ' } } |
Out-GridView

Search emails on date

Search for emails from a specific user after a specific date.

$User = "Amanda.Morgan@exoip.com"
$Date = "2024-01-01"

Get-MgUserMessage -All -UserId "$User" -Filter "ReceivedDateTime gt $Date" |
Select-Object Subject, InternetMessageId, ReceivedDateTime,
@{Name = "Sender"; Expression = { $_.Sender.EmailAddress.Address } }, 
@{Name = "Recipients"; Expression = { $_.ToRecipients.EmailAddress.Address -join ', ' } } |
Out-GridView

Search emails on subject

Search for emails that start with a specific subject.

$Subject = "The subject"
$user = "Amanda.Morgan@exoip.com"

Get-MgUserMessage -All -UserId "$User" -Filter "startswith(Subject,'$Subject')" |
Select-Object Subject, InternetMessageId, ReceivedDateTime,
@{Name = "Sender"; Expression = { $_.Sender.EmailAddress.Address } }, 
@{Name = "Recipients"; Expression = { $_.ToRecipients.EmailAddress.Address -join ', ' } } |
Out-GridView

Search for emails that are equal to the subject.

$Subject = "The specific subject"
$user = "Amanda.Morgan@exoip.com"

Get-MgUserMessage -All -UserId "$user" -Filter "Subject eq '$Subject'" | 
Select-Object Subject, InternetMessageId, ReceivedDateTime, 
@{Name = "Sender"; Expression = { $_.Sender.EmailAddress.Address } }, 
@{Name = "Recipients"; Expression = { $_.ToRecipients.EmailAddress.Address -join ', ' } } | 
Out-GridView

Search emails from last 30 days

Let’s search for all emails in the organization from the last 30 days, on the recipient and on the sender. The results will appear in an Out-GridView and be exported to a CSV file.

Search for all emails in the organization from the last 30 days.

# Get all users
$users = Get-MgUser -All

# Create an empty array to store the data
$Data = @()

# Determine the date 30 days prior to today
$startDate = (Get-Date).AddDays(-30).ToString("yyyy-MM-dd")

# Loop through each user
foreach ($user in $users) {
    # Ensure the user has an associated email address
    if ($user.Mail) {
        $messages = Get-MgUserMessage -UserId $user.Id -All -Filter "ReceivedDateTime ge $startDate" -ErrorAction SilentlyContinue
        foreach ($message in $messages) {
            $Data += [PSCustomObject]@{
                ReceivedDateTime  = $message.ReceivedDateTime
                Subject           = $message.Subject
                Sender            = $message.Sender.EmailAddress.Address -join ','
                Recipient         = $message.ToRecipients.EmailAddress.Address -join ','
                InternetMessageId = $message.InternetMessageId
            }
        }
    }
}

# Display and export the data
$Data | Out-GridView
$Data | Export-Csv -Path "C:\temp\All_emails.csv" -NoTypeInformation -Encoding utf8

Search for all emails in the organization that have been sent to a specific recipient in the last 30 days.

# Email address of the recipient you want to filter
$recipientEmailAddress = "amanda.morgan@exoip.com"

# Get all users
$users = Get-MgUser -All

# Create an empty array to store the data
$Data = @()

# Determine the date 30 days prior to today
$startDate = (Get-Date).AddDays(-30).ToString("yyyy-MM-dd")

# Loop through each user
foreach ($user in $users) {
    # Ensure the user has an associated email address
    if ($user.Mail) {
        $messages = Get-MgUserMessage -UserId $user.Id -All -Filter "ReceivedDateTime ge $startDate" -ErrorAction SilentlyContinue
        foreach ($message in $messages) {
            # Check if the recipient matches the desired email address
            if ($message.ToRecipients.EmailAddress.Address -eq $recipientEmailAddress) {
                $Data += [PSCustomObject]@{
                    ReceivedDateTime  = $message.ReceivedDateTime
                    Subject           = $message.Subject
                    Sender            = $message.Sender.EmailAddress.Address
                    Recipient         = $message.ToRecipients.EmailAddress.Address
                    InternetMessageId = $message.InternetMessageId
                }
            }
        }
    }
}

# Display and export the data
$Data | Out-GridView
$Data | Export-Csv -Path "C:\temp\Recipient_emails.csv" -NoTypeInformation -Encoding utf8

Search for all emails in the organization that have been sent from a specific sender in the last 30 days.

# Email address of the sender you want to filter
$senderEmailAddress = "john.doe@gmail.com"

# Get all users
$users = Get-MgUser -All

# Create an empty array to store the data
$Data = @()

# Determine the date 30 days prior to today
$startDate = (Get-Date).AddDays(-30).ToString("yyyy-MM-dd")

# Loop through each user
foreach ($user in $users) {
    # Ensure the user has an associated email address
    if ($user.Mail) {
        $messages = Get-MgUserMessage -UserId $user.Id -All -Filter "ReceivedDateTime ge $startDate" -ErrorAction SilentlyContinue
        foreach ($message in $messages) {
            if ($message.Sender.EmailAddress.Address -eq $senderEmailAddress) {
                $Data += [PSCustomObject]@{
                    ReceivedDateTime  = $message.ReceivedDateTime
                    Subject           = $message.Subject
                    Sender            = $message.Sender.EmailAddress.Address
                    Recipient         = ($message.ToRecipients.EmailAddress.Address -join ',')
                    InternetMessageId = $message.InternetMessageId
                }
            }
        }
    }
}

# Display and export the data
$Data | Out-GridView
$Data | Export-Csv -Path "C:\temp\Sender_emails.csv" -NoTypeInformation -Encoding utf8

That’s it!

Read more: Export Entra ID app registrations Certificates and Secrets expiry report »

Conclusion

You learned how to search emails with PowerShell. First, set up an application in Microsoft Entra with the correct permissions. After that, connect with Microsoft Graph PowerShell to the application. As last, run the commands to search for emails with PowerShell.

Did you enjoy this article? You may also like How to block Top-Level Domain (TLD) in Microsoft 365. Don’t forget to follow us and share this article.

Share This Article
Leave a comment