Monitor Office 365 admin role changes in all customer tenants

Connect Azure Function To Azure Storage and Microsoft Flow

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.

Sample email notification

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.

  1. To use this script, copy and paste it into Visual Studio Code and save it as a .ps1 file
  2. Enter your Azure Storage account name and primary key into the variables at the top of the script
  3. Press F5 to run it and enter the credentials of a delegated admin that has access to your Office 365 customer tenants.
  4. A report of all Admins, along with their roles and company details, will be exported to c:\temp\adminroles.csv
  5. The details for all admin users will also be uploaded to Azure Table Storage.

Logging admins

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
  1. 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

    Save PowerShell Modules To Computer

  2. Reconnect to your function via FTP and create a new folder under your new function. Call it modules.Create Modules Folder Via FTP in Function
  3. Upload the two PowerShell modules (MSOnline and AzureRMStorageTable) into the modules folder.Upload PowerShell Modules Via FTP
  4. 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.

  1. Open up Azure Storage Explorer (www.storageexplorer.com) and sign into it with the same account that you used to create your storage account.
  2. Select your subscription, locate your storage account and create a new queue by right clicking Queues and choosing Create Queue.Create Queue In Azure Storage Explorer
  3. Call it adminnotificationsCall Queue Adminnotifications

Create a Microsoft Flow to send the Queue messages

  1. Visit flow.microsoft.com and sign in with your Office 365 Account
  2. Click My Flows
  3. Click Create from blank
  4. Create a trigger by searching for queue and choosing Azure Queues – When there are messages in a queue.Check For Message In Azure Storage Account Queue
  5. Create a connection to your storage account by choosing a friendly name for it, then entering your storage account name and primary key.Connect To Azure Storage Account Queue
  6. Select the adminnotifications queue from the dropdown. You can also give your flow a name.Select Queue And Name Flow
  7. Add an Office 365 Outlook – Send an Email Step. Configure it to use your Office 365 account if required.Office 365 Send Email In Microsoft Flow
  8. 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 Message Text From Queue To Email
  9. 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.Delete Message From Azure Storage Queue
  10. Click Create Flow

Testing notifications on Office 365 admin role changes

  1. To test your solution, select your Azure Storage table from Storage Explorer (RoleMonitoring) and delete a recordDelete Message From Azure Storage Table
  2. Return to your Azure Function and click Run
  3. Wait for it to process. When the function gets to the missing record, a message will be added to your Azure Storage Queue.
  4. Microsoft Flow will pick up this message and send the contents of the message to you in an email.Sample email notification

Was this article helpful?

Related Articles