MS Graph and Exchange PowerShell Practical Application

exchange powershell practical application

All this time we’ve been talking about PowerShell for Graph and Exchange PowerShell. Now it is time for some practical application. The problem that lead me down this road was in relation to former employee mailboxes. Prior to this it was a manual process of finding former employee mailboxes, finding who had delegate access to those mailboxes, and emailing those managers to find out if they still needed access. I had three separate scripts that would do this, but it was all a manual process. Lets use what we have learned to see if we can combine those scripts into a single one that uses only application authentication.

To do this, I have to take some of what I talked about in my previous two posts regarding MS Graph PowerShell access. If you missed either of those, you can find them below:

*NOTE* If you are using a Dev Tenant like I am, Microsoft doesn’t allow emails to be sent (for what should be obvious reasons). If you want to fully test this, you can sign up for a test Azure tenant, and get the Microsoft 365 E1, E3, or E5 license trial to test these. Setting that up is outside the scope of this post, but there are plenty of instructions on how to do this if you search the web. Otherwise, you will see an error when you go to send emails.


DISCLAIMER

Please understand that the content herein is for informational purposes only. This existence and contents shall not create an obligation, liability or suggest a consultancy relationship. In further, such shall be considered as is without any express or implied warranties including, but not limited to express and implied warranties of merchantability, fitness for a particular purpose and non-infringement. There is no commitment about the content within the services that the specific functions of the services or its reliability, applicability or ability to meet your needs, whether unique or standard. Please be sure to test this process fully before deploying in ANY production capacity, and ensure you understand that you are doing so at your own risk.

Table of Contents

About this Termed Users Script
Script Variables and Connection Strings
Functions for the Script
Final Script
Conclusion

About This Termed Users Script

As I said the in the beginning, the intent of the script is to combine several processes needed to follow up with managers who have access to former employee mailboxes. The goal was to potentially automate it to the point that it could run as a service rather than me being involved at all. If I get to that point, I’ll be sure to come back and update all of you :).

Goals:

  • Gather the accounts of all former employees whose accounts still exist in AD (On Prem – Hybrid Environment)
  • Identify the managers who have access to those accounts
  • Email the managers to determine whether they still need access

Constraints

  • Emails still need to come from me and I want the replies to me as well
  • Needs to be dynamic – no manual intervention if possible
  • Leverages everything we’ve learned so far with PowerShell Application access to Azure.

Email Template

So you will notice in the final script that there is an HTML template that the script references. I do not have that template available, but that is because it is super easy to make one!

  1. Type an email in whatever mail client you prefer. (Must ensure the mail format is HTML)
  2. Send it to yourself.
  3. When the email arrives, view the raw source code for the email.
    1. In Outlook, you can right-click the email and choose ‘View Source’
    2. In Gmail, you can ‘Show Original’ (though it might be a little trickier finding the right code)
  4. From here, copy the HTML from the email, paste it into your favorite code editor (VS Code), and save it as an HTML file
  5. Open the file so you can see what it looks like. It should open in a browser, and look just like the email you sent.

That’s it! Super easy, and NO HTML coding required.

Other Notes

So my old script (which you haven’t seen) used to query a specific OU in Active Directory to pull terminated users in. That doesn’t work so well when running with MS Graph, so now my script for processing terminated users includes a step to add them to an AD group that includes only former employee accounts. This way, the new MS Graph script can just query accounts in this group. It isn’t a perfect solution, but it is a simple one and it works.

When you read the final script, the “$GroupName” variable refers to this group. So you will need all those former employee accounts to be in a single group to use this as is. Otherwise, modification is required.

Key PowerShell Cmdlet – Send-MgUserMail

This Cmdlet leverages the Microsoft.Graph.Users.Actions portion of the Microsoft Graph SDK. It leverages a JSON format to manage the variables. A base template is located here.

Application API Permissions

  1. First we need to add some new permissions to our Exchange PowerShell App-Only app registration.
  2. Navigate back to our app registration in Azure Active Directory. Click API permissions, and Add a permission. Choose Microsoft Graph –> Application Permissions, and add Mail.Send and User.ReadWrite.All to the list. These will be added to the existing Exchange.ManageAsApp permissions we already configured. Grant consent to the new permissions to continue.
api permissions for exchange powershell and ms graph

Once these permissions are in place, we are ready to check our variables and connection details.

Script Variables and Connection Strings

If you were following along for the Exchange Online Application access script, then you can just add to that one. If you are starting from scratch, grab the script below, and change the variables to match the necessary connection details.

#Connection Variables
$thumb = {"thumbprint"}
$client = {"clientID"}
$tenant = {"tenantID"}
$org = {"PrimaryDomain"}
$Certificate = Get-ChildItem Cert:\CurrentUser\My\$thumb

#Connection Strings
Connect-ExchangeOnline -CertificateThumbPrint $thumb -AppID $client -Organization $org
Connect-Graph -TenantId $tenant -AppId $client -Certificate $Certificate
  1. You can get the Application (client) ID and Directory (Tenant) ID from the Overview page from the app registration.
  2. The certificate thumbprint will be in the Manifest if you can’t find it on your local machine.
  3. The $org value can be found on the landing page of Azure Active Directory.

Our finished product should look like this to get connected.

connection strings for Exchange powershell and ms graph

Now, lets run this script and make sure we can connect to both successfully. Once both say they are connected successfully, you can run these commands to test connectivity.

#Test connectivity to Exchange online
Get-EXOMailbox | Select  Name,PrimarySmtpAddress,Guid

#Test connectivity to MS Graph
 Get-MgUser -Top 5

As long as you get results from these commands, you are connected successfully with the right permissions.

Functions for the Script

The first function to break down is the Send-TermMail function. This base for the script comes directly from Microsoft documentation. Available parameters are listed here. Just ensure you add new ones in the JSON format to match the existing ones.

function Send-TermMail {
    $to = $active
    $from = "[email protected]"
    $bcc = "[email protected]"
    $subject = "Termed Employee - " + $term.DisplayName
    $type = "html"
    $template = Get-Content -path "C:\temp\Template.html" -raw

    $params = @{
	    Message = @{
		    Subject = $subject
		    Body = @{
			    ContentType = $type
			    Content = $template
		    }
		    ToRecipients = @(
			    @{
				    EmailAddress = @{
					    Address = $to
				    }
			    }
		    )
		    BccRecipients = @(
			    @{
				    EmailAddress = @{
					    Address = $bcc
				    }
			    }
		    )
	    }
	    SaveToSentItems = "true"
    }  

Send-MgUserMail -UserId $from -BodyParameter $params
}

The next function can be found here. It is the Get-DelegateAccess function, and it pulls the list of users who have delegate mailbox access to the users pulled from the former employees group. These are our “managers” in this case.

function Get-DelegateAccess {
	#Function is used to get the members of the termed users group
	$termedUsers = Get-MgGroupMember -GroupId $group.Id #gets the members of the Termed Users group
	$delegateAccess = @()
	foreach ($tu in $termedUsers) {
		$tu2 = Get-MgUser -UserId $tu.id | select UserPrincipalName
		$m2 = Get-EXOMailboxPermission -Identity $tu2.userPrincipalName | Where-Object {($_.User -notlike "NT*") -and ($_.AccessRights -eq "FullAccess")} | select Identity,User
		$delegateAccess += $m2
	}
}

Our final function is Send-FollowUp function. This takes our list of managers with delegate access, and sends them all the template email based on their access.

function Send-FollowUp {
    foreach($person in $delegateAccess){
        #former employee
        $term = Get-EXOMailbox -Identity $person.identity | select DisplayName,userPrincipalName 
        #manager with delegate access
        $active = $person.user 
        Send-TermMail
    }  
}

That wraps up our functions. All that is left is to throw it all together into a final product.

Final Script

Our final script when it all comes together is listed below.

#Variables
$thumb = {"thumbprint"}
$client = {"clientID"}
$tenant = {"tenantID"}
$org = {"PrimaryDomain"}
$Certificate = Get-ChildItem Cert:\CurrentUser\My\$thumb
$GroupName = "Group Name" #Display Name of the group where termed users exist
$group = Get-MgGroup -Filter "DisplayName eq '$GroupName'" | select Id

#connection Strings
Connect-ExchangeOnline -CertificateThumbPrint $thumb -AppID $client -Organization $org
Connect-Graph -TenantId $tenant -AppId $client -Certificate $Certificate


#region functions
function Send-TermMail {
    $to = $active
    $from = "[email protected]"
    $bcc = "[email protected]"
    $subject = "Termed Employee - " + $term.DisplayName
    $type = "html"
    $template = Get-Content -path "C:\temp\Template.html" -raw

    $params = @{
	    Message = @{
		    Subject = $subject
		    Body = @{
			    ContentType = $type
			    Content = $template
		    }
		    ToRecipients = @(
			    @{
				    EmailAddress = @{
					    Address = $to
				    }
			    }
		    )
		    BccRecipients = @(
			    @{
				    EmailAddress = @{
					    Address = $bcc
				    }
			    }
		    )
	    }
	    SaveToSentItems = "true"
    }  

Send-MgUserMail -UserId $from -BodyParameter $params
}

function Get-DelegateAccess {
	#Function is used to get the members of the termed users group
	$termedUsers = Get-MgGroupMember -GroupId $group.Id #gets the members of the Termed Users group
	$delegateAccess = @()
	foreach ($tu in $termedUsers) {
		$tu2 = Get-MgUser -UserId $tu.id | select UserPrincipalName
		$m2 = Get-EXOMailboxPermission -Identity $tu2.userPrincipalName | Where-Object {($_.User -notlike "NT*") -and ($_.AccessRights -eq "FullAccess")} | select Identity,User
		$delegateAccess += $m2
	}
}

function Send-FollowUp {
    foreach($person in $delegateAccess){
        $term = Get-EXOMailbox -Identity $person.identity | select DisplayName,userPrincipalName #former employee
        $active = $person.user #manager with delgate access
        Send-TermMail
    }  
}

#endregion functions

#gather Delegate access details
Get-DelegateAccess

#Send the follow up email to the appropriate people
Send-FollowUp

Disconnect-ExchangeOnline -Confirm:$false
Disconnect-Graph

Alternatively, you can grab it from my GitHub page.

Conclusion

So there you have it! My first practical application for the MS Graph PowerShell SDK. Obviously this is a bit of a Niche use case, but the nice thing about this is that it could be totally automated without needing any user interaction. This also means it can be on a schedule, and doesn’t require you to remember to run it. I’m going to be looking for ways to securely save and run these scripts in an automated way as we go along, but for now we have the important part worked out.

So what do you think? Helpful script? Useful? Waste of time? I’d love to get some feedback. Let me know what you would change or add or remove.  As always, hit me up on Twitter @SeeSmittyIT to let me know what you thought of this post. Thanks for reading!

Smitty

Curtis Smith works in IT with a primary focus on Mobile Device Management, M365 Apps, and Azure AD. He has certifications from CompTIA and Microsoft, and writes as a hobby.

View all posts by Smitty →