<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Check If An Email Was Read Archives - the Sysadmin Channel</title>
	<atom:link href="https://thesysadminchannel.com/tag/check-if-an-email-was-read/feed/" rel="self" type="application/rss+xml" />
	<link>https://thesysadminchannel.com/tag/check-if-an-email-was-read/</link>
	<description>Documenting My Life as a System Administrator</description>
	<lastBuildDate>Wed, 23 Nov 2022 22:37:29 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
<site xmlns="com-wordpress:feed-additions:1">144174110</site>	<item>
		<title>Check If An Email Was Read using Graph API PowerShell SDK</title>
		<link>https://thesysadminchannel.com/how-to-check-if-an-email-was-read-using-graph-api-powershell-sdk/</link>
					<comments>https://thesysadminchannel.com/how-to-check-if-an-email-was-read-using-graph-api-powershell-sdk/#comments</comments>
		
		<dc:creator><![CDATA[Paul Contreras]]></dc:creator>
		<pubDate>Fri, 22 Jul 2022 23:36:41 +0000</pubDate>
				<category><![CDATA[Graph API]]></category>
		<category><![CDATA[Powershell]]></category>
		<category><![CDATA[Check If An Email Was Read]]></category>
		<guid isPermaLink="false">https://thesysadminchannel.com/?p=3378</guid>

					<description><![CDATA[<p>With the growing number of people migrating from the Azure AD module to Microsoft Graph API, it&#8217;s great to see some features finally become available via the command line interface .aka. PowerShell. Today we&#8217;ll cover step by step on how&#8230; <a href="https://thesysadminchannel.com/how-to-check-if-an-email-was-read-using-graph-api-powershell-sdk/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a href="https://thesysadminchannel.com/how-to-check-if-an-email-was-read-using-graph-api-powershell-sdk/">Check If An Email Was Read using Graph API PowerShell SDK</a> appeared first on <a href="https://thesysadminchannel.com">the Sysadmin Channel</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>With the growing number of people migrating from the Azure AD module to Microsoft Graph API, it&#8217;s great to see some features finally become available via the command line interface .aka. PowerShell.  Today we&#8217;ll cover step by step on how to check if an email was read using Graph API PowerShell SDK.<br />
&nbsp;</p>
<div id="tableofcontents">
<h2>Table Of Contents</h2>
<ul>
<li><a href="#requirements">Requirements</a></li>
<li><a href="#getreadstatus">How To Check If An Email Was Read using Graph API PowerShell SDK</a></li>
<ul>
<li><a href="#powershellscript">Get-MSGraphMailReadStatus PowerShell Script</a></li>
<li><a href="#parameters">Script Parameters</a></li>
<li><a href="#examples">Examples and Usage</a></li>
</ul>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</div>
<div id="requirements" style="scroll-margin-top: 15px;"></div>
<h2>Requirements</h2>
<p>The use case may vary, sometimes you might want to check how many people read a recent communication that was sent by the organization.  Other times you may need statistics for general read status.  Whatever the case may be, it can be helpful to get this kind of data.<br />
&nbsp;</p>
<p>In order to set this up and check to see if a specific email was read or not, there are certain requirements that need to be put in place in order to successfully query a mailbox.  Let&#8217;s cover those now.<br />
&nbsp;</p>
<ul>
<li>Microsoft.Graph PowerShell Module</li>
<li>EXACT subject line of the email you&#8217;re searching for</li>
<li>An Azure App Registration setup with following API permissions</li>
<ul>
<li>Directory.Read.All</li>
<li>Mail.ReadBasic.All</li>
</ul>
</ul>
<p><a href="https://thesysadminchannel.com/wp-content/uploads/2022/07/Mail-Read-Status-Graph-API-Permissions.png" target="_blank" rel="noopener"><img fetchpriority="high" decoding="async" src="https://thesysadminchannel.com/wp-content/uploads/2022/07/Mail-Read-Status-Graph-API-Permissions.png" alt="Mail Read Status Graph API Permissions" width="1028" height="220" class="aligncenter size-full wp-image-4319" srcset="https://thesysadminchannel.com/wp-content/uploads/2022/07/Mail-Read-Status-Graph-API-Permissions.png?v=1658460616 1028w, https://thesysadminchannel.com/wp-content/uploads/2022/07/Mail-Read-Status-Graph-API-Permissions-1024x219.png?v=1658460616 1024w, https://thesysadminchannel.com/wp-content/uploads/2022/07/Mail-Read-Status-Graph-API-Permissions-768x164.png?v=1658460616 768w" sizes="(max-width: 1028px) 100vw, 1028px" /></a><br />
&nbsp;</p>
<div id="blockquote1">
If you have never created an Azure App or need help getting started with Microsoft Graph API, please follow this guide on <a href="https://thesysadminchannel.com/how-to-connect-to-microsoft-graph-api-using-powershell/" rel="noopener" target="_blank">how to Connect To Microsoft Graph API Using PowerShell</a>.  It will take you from zero knowledge to getting everything up and running in no time.
</div>
<div id="getreadstatus" style="scroll-margin-top: 15px;"></div>
<h2>How To Check If An Email Was Read using Graph API PowerShell SDK</h2>
<p>Now that we have the Azure App Registration and Service Principal created with the above permissions, let&#8217;s look at how we can start getting message read status for our mail items.<br />
&nbsp;</p>
<div id="powershellscript" style="scroll-margin-top: 15px;"></div>
<h2>Get-MSGraphMailReadStatus PowerShell Script</h2>
<p>To give you some insight, this script uses <code>Get-MgUserMessage</code> graph cmdlet under the hood to get the actual message.  There is also a parameter to see which folder the mail item is currently located.  This helps if a user says they&#8217;ve lost an email, when in reality they&#8217;ve accidently dragged it into another folder without realizing it.<br />
&nbsp;</p>
<pre class="brush: powershell; title: ; notranslate">

Function Get-MgMailReadStatus {
&lt;#
.SYNOPSIS
    Get Email read status for users using Graph Api. A session using Connect-Graph must be open as a requirement.

.NOTES
    Name: Get-MgMailReadStatus
    Author: Paul Contreras
    Version: 1.3

.EXAMPLE
    Get-MgMailReadStatus -UserId user1@domain.com, user2@domain.com -Subject 'Exact Subject Line'

.EXAMPLE
    Get-MgMailReadStatus -UserId user1@domain.com -Subject 'Exact Subject Line' -SenderAddress user3@domain.com -StartDate 5/25/2020 -EndDate 10/28/2022 -ShowMailFolder

.PARAMETER UserId
    Checks against this persons mailbox.

.PARAMETER Subject
    Queries the mailbox using the exact text specified

.PARAMETER SenderAddress
    Specify the 'From' Address in your search.  Format should be user@domain.com

.PARAMETER StartDate
    Specify the date when the query should start filtering for.  Format should be MM/DD/YYYY

.PARAMETER EndDate
    Specify the date when the query should end the filter.  Format should be MM/DD/YYYY

.PARAMETER ShowMailFolder
    When this switch is used, it will display what folder the email is currently located in. This makes the overall query slower so use only when needed.

.PARAMETER Top
    Defaulted to 1.  This is the limit of emails per mailbox that you would like to find
#&gt;

    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            Position = 0
        )]
        [Alias('UserPrincipalName')]
        [string[]]  $UserId,


        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [string]  $Subject,


        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [string]  $SenderAddress,


        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [string]  $StartDate,


        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [string]  $EndDate,


        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [switch]  $ShowMailFolder,


        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [int]  $Top = 1,


        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [switch]    $IncludeRepliesandForwards
    )

    BEGIN {
        $ConnectionGraph = Get-MgContext
        if (-not $ConnectionGraph) {
            Write-Error &quot;Please connect to Microsoft Graph&quot; -ErrorAction Stop
        }

        if ($PSBoundParameters.ContainsKey('StartDate') -and -not $PSBoundParameters.ContainsKey('EndDate')) {
            Write-Error &quot;EndDate is required when a StartDate is entered&quot; -ErrorAction Stop
        }


        if ($PSBoundParameters.ContainsKey('EndDate') -and -not $PSBoundParameters.ContainsKey('StartDate')) {
            Write-Error &quot;StartDate is required when an EndDate is entered&quot; -ErrorAction Stop
        }

        if ($PSBoundParameters.ContainsKey('IncludeRepliesandForwards') -and -not $PSBoundParameters.ContainsKey('Subject')) {
            Write-Error &quot;A Subject is required to use IncludeRepliesandForwards&quot; -ErrorAction Stop
        }

        $Date = Get-Date -Format g
    }

    PROCESS {
        foreach ($User in $UserId) {
            try {
                $GraphUser = Get-MgUser -UserId $User -ErrorAction Stop | select Id, DisplayName, UserPrincipalName
                Write-Verbose &quot;Checking Status for $($GraphUser.DisplayName)&quot;

                #Building the filter query
                if ($PSBoundParameters.ContainsKey('Subject')) {
                    if ($Subject -match &quot;'&quot;) {
                        $Subject = $Subject -replace &quot;'&quot;,&quot;''&quot;
                    }

                    if (-not $PSBoundParameters.ContainsKey('IncludeRepliesandForwards')) {
                        $FilterQuery = &quot;Subject eq '$Subject'&quot;
                    }
                }


                if ($PSBoundParameters.ContainsKey('SenderAddress')) {
                    if ($FilterQuery) {
                        $FilterQuery = $FilterQuery + &quot; AND from/emailAddress/address eq '$SenderAddress'&quot;
                    } else {
                        $FilterQuery = &quot;from/emailAddress/address eq '$SenderAddress'&quot;
                    }
                }


                if ($PSBoundParameters.ContainsKey('StartDate') -and $PSBoundParameters.ContainsKey('EndDate')) {
                    $BeginDateFilter = (Get-Date $StartDate).AddDays(-1).ToString('yyyy-MM-dd') #Adding a day to either side to account for UTC time and MS operators.
                    $EndDateFilter = (Get-Date $EndDate).AddDays(1).ToString('yyyy-MM-dd')

                    if ($FilterQuery) {
                        $FilterQuery = $FilterQuery + &quot; AND ReceivedDateTime ge $BeginDateFilter AND ReceivedDateTime le $EndDateFilter&quot;
                    } else {
                        $FilterQuery = &quot;ReceivedDateTime ge $BeginDateFilter AND ReceivedDateTime le $EndDateFilter&quot;
                    }
                }


                if ($FilterQuery) {
                    if ($Top -gt 10) {
                        $Message = Get-MgUserMessage -UserId $User -Filter $FilterQuery -Top $Top -ErrorAction Stop | Sort-Object ReceivedDateTime -Descending
                      } else {
                        $Message = Get-MgUserMessage -UserId $User -Filter $FilterQuery           -ErrorAction Stop | Sort-Object ReceivedDateTime -Descending | select -First $Top
                    }

                    if ($PSBoundParameters.ContainsKey('IncludeRepliesandForwards')) {
                        $Message = $Message | Where-Object {$_.Subject -like &quot;* $Subject&quot;}
                    }
                } else {
                    $Message = Get-MgUserMessage -UserId $User -ErrorAction Stop | Sort-Object ReceivedDateTime -Descending | select -First $Top
                }


                #Building output object
                $Object = [Ordered]@{
                    RunDate            = $Date
                    MailboxDisplayName = $GraphUser.DisplayName
                    Mailbox            = $GraphUser.UserPrincipalName
                    UserId             = $GraphUser.Id
                    SenderAddress      = $null
                    SenderName         = $null
                    RecipientAddress   = $null
                    RecipientName      = $null
                    Subject            = $null
                    IsRead             = $null
                    HasAttachment      = $null
                    ReceivedDate       = $null
                    MessageId          = $null
                }

                if ($PSBoundParameters.ContainsKey('ShowMailFolder')) {
                    $Object.Add( 'MailFolder', $null )
                    $Object.Add( 'PSTypeName', 'GraphMailReadStatus.MailFolder')
                  } else {
                    $Object.Add( 'PSTypeName', 'GraphMailReadStatus')
                }

                if (-not $Message) {
                    Write-Verbose &quot;0 Messages with the subject: '$Subject' were found on Mailbox: '$($GraphUser.DisplayName)'&quot;

                    #Output object
                    [PSCustomObject]$Object

                } else {
                    Write-Verbose &quot;$($Message.Count) Message(s) with the subject: '$Subject' were returned on Mailbox: '$($GraphUser.DisplayName)'&quot;
                    foreach ($MailItem in $Message) {
                        $Object.SenderAddress      = $MailItem.sender.emailaddress | select -ExpandProperty Address
                        $Object.SenderName         = $MailItem.sender.emailaddress | select -ExpandProperty Name
                        $Object.RecipientAddress   = $MailItem.toRecipients.emailaddress | select -ExpandProperty Address
                        $Object.RecipientName      = $MailItem.toRecipients.emailaddress | select -ExpandProperty Name
                        $Object.Subject            = $MailItem.Subject
                        $Object.IsRead             = $MailItem.IsRead
                        $Object.HasAttachment      = $MailItem.HasAttachments
                        $Object.ReceivedDate       = $MailItem.ReceivedDateTime.ToLocalTime()
                        $Object.MessageId          = $MailItem.Id

                        if ($PSBoundParameters.ContainsKey('ShowMailFolder')) {
                            $MailFolder = Get-MgUserMailFolder -MailFolderId $MailItem.ParentFolderId -UserId $GraphUser.UserPrincipalName | select -ExpandProperty DisplayName

                            $Object.MailFolder = $MailFolder
                            $Object.PSTypeName = 'GraphMailReadStatus.MailFolder'

                          } else {
                            $Object.PSTypeName = 'GraphMailReadStatus'

                        }

                        [PSCustomObject]$Object

                        $Message    = $null
                        $MailItem   = $null
                        $MailFolder = $null
                    }
                }

            } catch {
                Write-Error $_.Exception.Message

            }

            #end foreach block
            $Object = $null
        }
    }

    END {}
}

</pre>
<div id="parameters" style="scroll-margin-top: 15px;"></div>
<h2>Script Parameters</h2>
<h3>    -UserId</h3>
<p>DataType: string/array<br />
Description: Checks against this persons mailbox.  Multiple UPNs/ObjectIds separated by a comma are acceptable.<br />
&nbsp;</p>
<h3>    -Subject</h3>
<p>DataType: string<br />
Description: Queries the mailbox using the EXACT text specified<br />
&nbsp;</p>
<h3>    -SenderAddress</h3>
<p>DataType: string<br />
Description: Specify the &#8216;From&#8217; Address in your search.  Format should be user@domain.com<br />
&nbsp;</p>
<h3>    -StartDate</h3>
<p>DataType: string<br />
Description: Specify the date when the query should start filtering for.  Format should be MM/DD/YYYY<br />
&nbsp;</p>
<h3>    -EndDate</h3>
<p>DataType: string<br />
Description: Specify the date when the query should end the filter.  Format should be MM/DD/YYYY<br />
&nbsp;</p>
<h3>    -ShowMailFolder</h3>
<p>DataType: switch<br />
Description: When this switch is used, it will display what folder the email is currently located in. This makes the overall query slower so use only when needed<br />
&nbsp;</p>
<h3>    -Top</h3>
<p>DataType: int<br />
Description: Defaulted to 1.  This is the limit of emails per mailbox that you would like to find<br />
&nbsp;</p>
<div id="examples" style="scroll-margin-top: 15px;"></div>
<h3>Example 1 &#8211; Specifying a user, a subject and the parent folder</h3>
<pre class="brush: powershell; gutter: false; title: ; notranslate">
PS C:\&gt; Get-MSGraphMailReadStatus -UserId paul@thesysadminchannel.com `
-Subject &quot;You’ve renewed your Office 365 E1 subscription&quot; -ShowMailFolder
</pre>
<p><a href="https://thesysadminchannel.com/wp-content/uploads/2022/07/Get-Mail-Read-Status-Example-1.png" target="_blank" rel="noopener"><img decoding="async" src="https://thesysadminchannel.com/wp-content/uploads/2022/07/Get-Mail-Read-Status-Example-1.png" alt="Get Mail Read Status Example 1" width="845" height="398" class="aligncenter size-full wp-image-4329" srcset="https://thesysadminchannel.com/wp-content/uploads/2022/07/Get-Mail-Read-Status-Example-1.png?v=1658465834 845w, https://thesysadminchannel.com/wp-content/uploads/2022/07/Get-Mail-Read-Status-Example-1-768x362.png?v=1658465834 768w" sizes="(max-width: 845px) 100vw, 845px" /></a></p>
<div id="conclusion" style="scroll-margin-top: 15px;"></div>
<h2>Conclusion</h2>
<p>Hopefully this article was able to show you how to check if an email was read using Graph API PowerShell.  Since this utilizes Graph API, it supports PowerShell 7 and can be incredible fast when using Foreach-Object -Parallel.  I&#8217;ve used this several times in my environment and its great addition to my tool belt.</p>
<p>The post <a href="https://thesysadminchannel.com/how-to-check-if-an-email-was-read-using-graph-api-powershell-sdk/">Check If An Email Was Read using Graph API PowerShell SDK</a> appeared first on <a href="https://thesysadminchannel.com">the Sysadmin Channel</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://thesysadminchannel.com/how-to-check-if-an-email-was-read-using-graph-api-powershell-sdk/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3378</post-id>	</item>
	</channel>
</rss>
