How to Get Started with an Azure Automation Account pt. 2

azure automation accounts

Now it is time to put what we know about Azure Automation Accounts to work. One of the driving factors behind my wanting to learn PowerShell has always been to make my life easier. I am constantly looking for ways to automate the tedious and repetitive tasks in my day-to-day life. In a previous post, I wrote about a PowerShell script to mass email managers with Delegate access to the mailboxes of former employees. In that post, I left the script as something to be run manually when needed. Since then, I’ve decided this is an excellent candidate for an Azure Automation Account.

Why not enable it to run automatically, once a month on a schedule, and then send me an email to let me know? There is no reason not to do this, and so I decided to do it. It required a rewrite of the script, but that is ok, it is worth it if it means I can set it and mostly forget it. So today, that is what we are looking at doing.

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

Prerequisites for Termed Follow Up Script
Configure Azure Account with Appropriate Roles
Adding Modules to Automation Account
Updates to the Script
Conclusion


Prerequisites for Termed Follow Up Script

Before we begin, I’m making a few assumptions. This is a continuation of an existing solution. I’ll give the general requirements here, but everything I’m assuming has been done, is covered in detail in the last post. If you followed along with the last example, I had for this, then you already have pretty much everything in place. If not, here is what you need:

  • I have my terminated user process including a step to add all termed users to a separate group (after removing all other groups) and making it their primary group. This process makes that assumption as well, as I believe the group is an easier alternative for keeping track of termed accounts. Additionally, you’ll need the Azure ID of this group for a section in the script.
  • I already have an app registration with the appropriate permissions. If you need help creating one, I have a post here where I talk about it. I recommend creating a new one just for this use case if you don’t already have one. The permissions you need are as follows:
    • Microsoft Graph
      • Mail.Send – Application Access
      • User.Read.All – Application Access
  • An Email template of the message you want to send to the current employees/managers with delegate access.

That should cover the basics. Anything else we need from here can be created as we go along.


Configure Azure Role for Automation Account

One thing we will need for this script is to assign an Azure role to the Automation Account, so we are able to connect to Exchange Online with the Managed Identity. Using the managed identity of the Automation Account makes it easier to audit access and reduces the chances of credentials being leaked or reused.

  1. In the Azure Portal, search Azure Active Directory, and navigate there. From the left-hand list, choose “Roles and Administrators”.
  2. From here, we want to search for the “Exchange Recipient Administrator” role. This will give our Automation Account these permissions: “Users with this role have read access to recipients and write access to the attributes of those recipients in Exchange Online.” You could create a custom role for this, and assign specific permissions, but I typically try to stick to built-in roles whenever possible, so I just attempted to choose the one with the fewest permissions needed.
configure role for azure automation account
  1. Click Add assignments, and search by name for the Automation Account (it won’t be listed without the search). Select the account and hit Add.

Adding this now will give Azure AD time to sync for the permissions to go into effect. It took about 15 minutes before this worked for me.

*NOTE* If you are planning on assigning Azure Roles to an Automation Account (Like for this example) you will want to ensure you aren’t creating any runbooks under this account that don’t need this role. Ideally, the only scripts that run under this account, leverage the permissions they have access to. Other scripts should have a separate Automation Account, with its own permissions. Principal of Least privilege only works if you apply it everywhere!

Additional Automation Details

We also need to add some important information to the Variables section in our Automation Account. Navigate to Shared Resources –> Variables. Add a new Variable.

azure automation account variable examples

In this section, you will need to add the Client ID, Tenant ID, Term Group ID and Secret as a saved variable. Be sure to Encrypt each of these values for maximum security. It is better to have them encrypted than to leave anything that will allow a malicious actor to send emails in your tenant. You can always change the information and come back and update this later. Be sure to save each variable and make a note of the name. You’ll need to know it for the script. It should look like this when you are done (variable names may be different)

azure automation account variable examples

Adding Modules to Automation Account

  1. In the Azure Portal, go back to your automation account and scroll down the left-hand side until you get to Shared Resources. You should see several things that matter to what we are trying to accomplish. We will configure out schedule last, so lets start with Modules.
  2. Once you click on Modules, you’ll notice that all of the Azure Modules are pre-loaded. It will allow you to clean these up, although there isn’t much benefit. Click “Add a Module”.
  3. Change the radio button to “Add from Gallery” and search for “ExchangeOnlineManagement”.
exchange online management module for azure automation account runbook
  1. Now, select this module, and it will list all the cmdlets you get with this module. Click select to finish the selection. Once back to the modules page, you will need to select a runtime. ExchangeOnlineManagement is supported for all the versions listed, but for this I chose 7.1. (Trying to avoid updates later).
    • The key thing with the runtime version, is that the runbook will need to match the runtime version for the modules. If you wrote your code for 7.2, don’t expect everything will work automatically if you choose 5.1 for your runtime and vice versa.
  2. Once this is installed successfully, we are ready to input our code into a runbook.

Updates/Changes to the Script

The original script is located HERE. This was the basis for this script, though it did change quite a bit as I tried to implement this with the automation account. One thing I found is that I kept getting errors when trying to connect to both PowerShell Graph SDK and Exchange Online Management via PowerShell. For me, the solution was to allow the script to attempt to connect to Exchange Online Management using the managed identity of the automation account and to use Invoke-RestMethod for all of my MS Graph calls and email send.

Additionally, I also found that it was far more valuable to include the HTML for the email template directly in the script. This way I didn’t have to worry about the email template file needing to be saved somewhere. Let’s look at the changes, then I’ll include the link to the final script.

Ensure you include the variables you added earlier

Remember how we added those variables earlier? Now it is time to call them to the script. The automation account can retrieve these encrypted values and ensure they are used without ever being exposed to anyone who may edit the script later.

#Get Variables from AA Keyvault to get connected
$Client = Get-AutomationVariable -Name 'ClientID'  #Client ID of the App Registration
$secret = Get-AutomationVariable -Name 'Secret'    #Secret from the App Registration
$tenant = Get-AutomationVariable -Name 'TenantId' #Call the Automation Variable for the Tenant ID
$groupID = Get-AutomationVariable -Name 'GroupId'  #call the automation variable for the group ID

Test the Search Criteria to Make Sure you Get the right members with Delegate Access

In this section of the script, I have it searching for all the accounts that have delegate access to a mailbox in the Termed Users group.

foreach ($tu in $values) {
    $tu2 = $tu.userPrincipalName
#Pay special attention to this line, here is where you will need to test to see if there are any service or backup accounts that may be returned with this $m2 variable command, and adjust the filtering so it matches your environment. Just add another {($_.User -notlike "") to this where-object of this next line
	$m2 = Get-EXOMailboxPermission -Identity $tu2 -ErrorAction SilentlyContinue | Where-Object {($_.User -notlike "NT*") -and ($_.AccessRights -eq "FullAccess")} | Select-Object Identity,User
    $delegateCount = $m2 | Measure-Object | Select-Object Count
    
    #some termed user accounts have more than one person with delegate access, so this examines that and ensures that each person with delegate access will get an email
    if ($delegateCount.Count -ge 2 ) {
        foreach($mm in $m2){
            $urow = $delegateAccess.NewRow()
            $urow.termed = $mm.Identity
            $urow.Delegate = $mm.user
            $delegateAccess.Rows.Add($urow)
        }
    }else {
        $urow = $delegateAccess.NewRow()
        $urow.termed = $m2.Identity
        $urow.Delegate = $m2.user
        $delegateAccess.Rows.Add($urow)
    }
}

Update the Email Template Area with your Custom Message

This section is where you can update the custom message you will send. It accepts HTML and you can either write your own, or just send yourself the email, then right click -> Show Source and copy and paste that code.

#Term Template Email Content
$termTemplate = @"
Your Message is Copied here <br>
This message will be the email <br>
Show the source of your existing template to get the HTML code that goes between these lines
"@

Include a simple email to get notified when this runbook runs

I added this one-liner here so that I would get an email with the number of managers that had delegate access. Feel free to remove this if you didn’t want to know.

#sends the final email report as a message to me so I know how mant emails went out. 
Send-MailMessage -upn "[email protected]" -subject "Term Email Report" -content "<h2>Term Follow Up Emails sent: $emailSent</h2>"

Finally, the full version of the script can be found here. AA-FormerEmployeeFollowUp.ps1 · SeeSmitty/Powershell (github.com)

Copy that code, tweak it to match what you modified, and give it a schedule. I recommend testing it by changing “Term.delegate” to your email while you run this in the Runbook testing section before you unleash it on your unsuspecting recipients.

#Cycle through the list of termed users and send an email to each manager with delegate access to that mailbox
foreach($term in $delegateAccess){
    $displayname = $term.Termed
    $termSubject = "Former Employee Follow Up - $displayname"
    Send-MailMessage -upn $term.delegate -subject $termSubject -content $termTemplate
    $emailSent += 1
}

Conclusion

There you have it. This will likely take a little bit of effort on your part to get everything working exactly as I wrote it. That is ok though! The purpose here is to give you an example of what is possible, so you can learn and write your own automation scripts. This one just worked for me because it took a repetitive process, that doesn’t change, and turned it into something I can set and forget. Obviously, I’ll need to remember to key the security keys up to date and ensure that I’m checking periodically to make sure it runs successfully. I’ll take that over the manual efforts I had before!

What do you think? Is this something you would be interested in trying? Or does this inspire you write your own runbooks. Let me know, I can’t wait to hear your ideas! Hit me up on Twitter @SeeSmittyIT to let me know what you thought of this post. Or if you are avoiding the bird site, I’m also posted up on Mastodon @[email protected]. 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 →