Monitor Office 365 admin role changes in all customer tenants
If you’re managing Office 365 tenants as a Microsoft Partner, it’s a good idea to keep track of any admin role changes within those tenants.
This guide will demonstrate how to use PowerShell and Azure Functions to check the admin role members for each of your customer tenants. This solution will create a record of all current admin role members and compare your customers admins against this record every day. If an admin is added or removed in a customer tenant, a message will be added to an Azure Storage Queue. A Microsoft Flow will then be triggered, and email someone to notify them of the role change.
Feel free to modify this solution to suit your own requirements.
Prerequisites
Before you get started, you’ll need to do the following:
- Create an Azure Storage Account and retrieve its account name and primary key.
- Once you’ve created your storage account, I recommend using the Azure Storage Explorer to work with it. Download it from here: www.storageexplorer.com
- Install the AzureRMStorageTable PowerShell Module. Open PowerShell as an Administrator and run:
Install-Module -Name AzureRmStorageTable
- Install the MSOnline PowerShell Module.
Install-Module -Name MSOnline
Report on existing admins in all Office 365 customer tenants
Use this PowerShell script to report on all current Office 365 admins in your customer’s tenants.
- To use this script, copy and paste it into Visual Studio Code and save it as a .ps1 file
- Enter your Azure Storage account name and primary key into the variables at the top of the script
- Press F5 to run it and enter the credentials of a delegated admin that has access to your Office 365 customer tenants.
- A report of all Admins, along with their roles and company details, will be exported to c:\temp\adminroles.csv
- The details for all admin users will also be uploaded to Azure Table Storage.
Script to report on, and upload, Office 365 admin details to Azure table storage
Import-Module AzureRmStorageTable # Set this $NotifyOnNewAdmins value to true after you've completed the initial upload $NotifyOnNewAdmins = $false $storageAccount = "STORAGEACCOUNTNAME" $primaryKey = "STORAGEACCOUNTPRIMARYKEY" $tableName = "roleMonitoring" $queueName = "adminnotifications" $exportPath = "C:\temp\adminroles.csv" $Ctx = New-AzureStorageContext $storageAccount -StorageAccountKey $primaryKey $Table = Get-AzureStorageTable -Name $tableName -Context $ctx -ErrorAction Ignore if ($Table -eq $null) { $Table = New-AzureStorageTable -Name $tableName -Context $Ctx } $Queue = Get-AzureStorageQueue -Name $QueueName -Context $Ctx -ErrorAction Ignore if ($Queue -eq $null) { $Queue = New-AzureStorageQueue -Name $QueueName -Context $Ctx } # Check for existing Admins $existingAdmins = (Get-AzureStorageTableRowAll -table $Table).RowKey Connect-MsolService $customers = Get-MsolPartnerContract -all $currentAdmins = @() foreach ($customer in $customers) { $roles = Get-MsolRole -tenantID $customer.tenantid $admins = @() foreach ($role in $roles) { $members = $null #Write-Host "Checking $($role.Name) for $($customer.name)" -ForegroundColor Yellow $members = Get-MsolRoleMember -RoleObjectId $role.objectid -TenantId $customer.TenantId | Where-Object {$_.rolemembertype -contains "User"} if ($members) { Write-Host "Found $($members.count) $($role.name)s in $($customer.name)" -ForegroundColor Blue foreach ($member in $members) { $member | Add-Member Id "$($role.objectid)-$($member.emailaddress)" $member | add-member RoleName $role.Name $currentAdmins += $member $filter = $null $filter = $existingAdmins | Where-Object {$_ -contains $member.Id} if($filter){ Write-Host "$($member.DisplayName) has already been logged as a $($member.RoleName)" -ForegroundColor Green }else{ Write-Host "New Admin Detected: $($member.DisplayName) - $($member.RoleName)" -ForegroundColor Yellow $admins += $member } } } } if ($admins) { foreach ($admin in $admins) { $adminHash = @{ CustomerName = $customer.name TenantID = $customer.TenantId RoleName = $admin.RoleName DisplayName = $admin.DisplayName EmailAddress = $admin.emailaddress IsLicensed = $admin.IsLicensed } Add-AzureStorageTableRow -rowKey $admin.id -partitionKey "Admin" -table $table -property $adminHash if($NotifyOnNewAdmins){ $QueueMessageText = "New $($admin.RoleName) detected: `nCompany Name: $($customer.name) `nDisplay Name: $($admin.displayname) `nEmail Address: $($admin.EmailAddress) `nIs licensed: $($admin.IsLicensed)" $QueueMessage = New-Object -TypeName Microsoft.WindowsAzure.Storage.Queue.CloudQueueMessage -ArgumentList $QueueMessageText $Queue.CloudQueue.AddMessage($QueueMessage) } $exportObject = New-Object PSObject -property $adminHash $exportObject | Export-Csv $exportPath -NoTypeInformation -Append } } }
How to monitor Office 365 admin role changes indefinitely
You can use Azure Functions and Microsoft Flow to be alerted to role changes each day.
Create an Azure Function that can connect to Office 365 via PowerShell
First, you’ll need to create an Azure Function app that can connect to Office 365.
Follow this guide to create one, then create a timer triggered PowerShell Azure Function.
Use the guide to complete the following:
- Create a Timer Triggered PowerShell Azure Function called MonitorAdminRoles and set it to run on a schedule eg. 0 0 22 * * * (Run daily at 10PM GMT time)
- Encrypt and upload the key for your Office 365 admin credentials via FTP
- Add the username and encrypted password into the application properties
- Note: don’t worry about uploading the MSOnline module into the ‘bin’ folder
- Next, open PowerShell on your computer and download the MSOnline and AzureRMStorageTable powershell modules using the following cmdlets.
Save-Module AzureRMStorageTable -Path C:\temp Save-Module MSOnline -Path C:\temp
- Reconnect to your function via FTP and create a new folder under your new function. Call it modules.
- Upload the two PowerShell modules (MSOnline and AzureRMStorageTable) into the modules folder.
- Copy the code below into your Azure Function, update it with your storage account name and primary key, then save your Azure Function
Script for PowerShell Azure Function to monitor Office 365 admins in customer tenants
Write-Output "PowerShell Timer trigger function executed at:$(get-date)"; # Set this $NotifyOnNewAdmins value to true after you've completed the initial upload $NotifyOnNewAdmins = $true $storageAccount = "STORAGEACCOUNTNAME" $primaryKey = "STORAGEACCOUNTPRIMARYKEY" $tableName = "roleMonitoring" $queueName = "adminnotifications" $FunctionName = "MonitorAdminRoles" $username = $Env:user $pw = $Env:password # Build Credentials $keypath = "D:\home\site\wwwroot\$FunctionName\bin\keys\PassEncryptKey.key" $secpassword = $pw | ConvertTo-SecureString -Key (Get-Content $keypath) $credential = New-Object System.Management.Automation.PSCredential ($username, $secpassword) # Connect to MSOnline Connect-MsolService -Credential $credential $Ctx = New-AzureStorageContext $storageAccount -StorageAccountKey $primaryKey $Table = Get-AzureStorageTable -Name $tableName -Context $ctx -ErrorAction Ignore if ($Table -eq $null) { $Table = New-AzureStorageTable -Name $tableName -Context $Ctx } $Queue = Get-AzureStorageQueue -Name $QueueName -Context $Ctx -ErrorAction Ignore if ($Queue -eq $null) { $Queue = New-AzureStorageQueue -Name $QueueName -Context $Ctx } # Check for existing Admins $existingAdmins = (Get-AzureStorageTableRowAll -table $Table).RowKey $customers = Get-MsolPartnerContract -all $currentAdmins = @() foreach ($customer in $customers) { $roles = Get-MsolRole -tenantID $customer.tenantid $admins = @() foreach ($role in $roles) { $members = $null $members = Get-MsolRoleMember -RoleObjectId $role.objectid -TenantId $customer.TenantId | Where-Object {$_.rolemembertype -contains "User"} if ($members) { Write-Output "Found $($members.count) $($role.name)s in $($customer.name)" foreach ($member in $members) { $member | Add-Member Id "$($role.objectid)-$($member.emailaddress)" $member | add-member RoleName $role.Name $currentAdmins += $member $filter = $null $filter = $existingAdmins | Where-Object {$_ -contains $member.Id} if($filter){ Write-Output "$($member.DisplayName) has already been logged as a $($member.RoleName)" }else{ Write-Output "New Admin Detected: $($member.DisplayName) - $($member.RoleName)" $admins += $member } } } } if ($admins) { foreach ($admin in $admins) { $adminHash = @{ CustomerName = $customer.name TenantID = $customer.TenantId RoleName = $admin.RoleName DisplayName = $admin.DisplayName EmailAddress = $admin.emailaddress IsLicensed = $admin.IsLicensed } Add-AzureStorageTableRow -rowKey $admin.id -partitionKey "Admin" -table $table -property $adminHash if($notifyOnNewAdmins){ $QueueMessageText = "New $($admin.RoleName) detected: `nCompany Name: $($customer.name) `nDisplay Name: $($admin.displayname) `nEmail Address: $($admin.EmailAddress) `nIs licensed: $($admin.IsLicensed)" $QueueMessage = New-Object -TypeName Microsoft.WindowsAzure.Storage.Queue.CloudQueueMessage -ArgumentList $QueueMessageText $Queue.CloudQueue.AddMessage($QueueMessage) } } } }
Create an Azure Storage Queue to receive notifications
Next we’ll create a Storage Queue that can receive messages about role changes.
- Open up Azure Storage Explorer (www.storageexplorer.com) and sign into it with the same account that you used to create your storage account.
- Select your subscription, locate your storage account and create a new queue by right clicking Queues and choosing Create Queue.
- Call it adminnotifications
Create a Microsoft Flow to send the Queue messages
- Visit flow.microsoft.com and sign in with your Office 365 Account
- Click My Flows
- Click Create from blank
- Create a trigger by searching for queue and choosing Azure Queues – When there are messages in a queue.
- Create a connection to your storage account by choosing a friendly name for it, then entering your storage account name and primary key.
- Select the adminnotifications queue from the dropdown. You can also give your flow a name.
- Add an Office 365 Outlook – Send an Email Step. Configure it to use your Office 365 account if required.
- Add in an email address to receive the notifications, choose a subject, then drag in the Message Text from the queue parameters in the dynamic content menu.
- Add in an Azure Queues – Delete Message step. This will remove the message from the queue once it has been processed. Choose the queue name and drag in the Message ID and Pop Receipt values.
- Click Create Flow
Testing notifications on Office 365 admin role changes
- To test your solution, select your Azure Storage table from Storage Explorer (RoleMonitoring) and delete a record
- Return to your Azure Function and click Run
- Wait for it to process. When the function gets to the missing record, a message will be added to your Azure Storage Queue.
- Microsoft Flow will pick up this message and send the contents of the message to you in an email.
Leave a Reply
Want to join the discussion?Feel free to contribute!