Retrieve customers’ Office 365 Message Center info via PowerShell and the Office 365 Management API
Occasionally Microsoft will roll out an update that requires Office 365 admins to prepare for a change by a certain date.

In these situations the roll-out dates are often staggered over a period of time, and if you’re managing a bunch of Office 365 tenants it can be difficult to determine when the change will actually occur.
Recently, I had a couple of requests from other partners to help determine when the Skype for Business to Microsoft Teams upgrade would take place for their customers. This information is communicated to customers via email, however the email doesn’t include any information identifying the customer it refers to.
Fortunately, this information is recorded in the Office 365 Message Center which is now accessible via the Office 365 Management API.
Here is a script that Microsoft Partners can use to retrieve the ‘Action required by’ dates for specific updates in all customer tenants.
The script works by supplying a string of text to match against the messages in your customers’ Office 365 Message Centers. eg: “Microsoft will automatically upgrade your users to Teams”
If a matching message is found, details for the customer, message and the ‘action required by’ date are exported to a csv.
Prerequisites
- You’ll need Global Admin permissions on your own Tenant
- You’ll need to be a Microsoft Partner with delegate admin access to customer tenants from your own tenant
- You’ll need to have the AzureAD PowerShell module installed on your computer
How to run the script
- Double click the script below to select it.
- Copy and paste it into a new file in Visual Studio Code
- Save it with a .ps1 extension
- Install the recommended PowerShell Module if you haven’t already
- Update the text in $MatchingString to match a line of text in the update you’re interested in.

- Update the $homePage and $appIdURI values to a valid domain within your own tenant. eg https://yourdomain.com/$((New-Guid).ToString())

- Press F5 to run the script.
- Sign into a global admin account for your own tenant.
- Wait for it to complete.

- A CSV with the relevant info will be exported to C:tempMessageCenterDetails.csv

Script to retrieve customers’ Office 365 Message Center messages via the Office 365 Management API
$MatchingString = "Microsoft will automatically upgrade your users to Teams"
$AppName = "GCITS Service Communications Reader"
Connect-AzureAd
Function AddResourcePermission($requiredAccess, `
$exposedPermissions, [string]$requiredAccesses, [string]$permissionType) {
foreach ($permission in $requiredAccesses.Trim().Split("|")) {
foreach ($exposedPermission in $exposedPermissions) {
if ($exposedPermission.Value -eq $permission) {
$resourceAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess
$resourceAccess.Type = $permissionType
$resourceAccess.Id = $exposedPermission.Id
$requiredAccess.ResourceAccess.Add($resourceAccess)
}
}
}
}
Function GetRequiredPermissions($requiredDelegatedPermissions, $requiredApplicationPermissions, $reqSP) {
$sp = $reqSP
$appid = $sp.AppId
$requiredAccess = New-Object Microsoft.Open.AzureAD.Model.RequiredResourceAccess
$requiredAccess.ResourceAppId = $appid
$requiredAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess]
if ($requiredDelegatedPermissions) {
AddResourcePermission $requiredAccess -exposedPermissions $sp.Oauth2Permissions -requiredAccesses $requiredDelegatedPermissions -permissionType "Scope"
}
if ($requiredApplicationPermissions) {
AddResourcePermission $requiredAccess -exposedPermissions $sp.AppRoles -requiredAccesses $requiredApplicationPermissions -permissionType "Role"
}
return $requiredAccess
}
Function CreateAppRole($Name, $Description) {
$appRole = New-Object Microsoft.Open.AzureAD.Model.AppRole
$appRole.AllowedMemberTypes = New-Object System.Collections.Generic.List[string]
$appRole.AllowedMemberTypes.Add("User");
$appRole.DisplayName = $Name
$appRole.Id = New-Guid
$appRole.IsEnabled = $true
$appRole.Description = $Description
$appRole.Value = $Name;
return $appRole
}
Function CreateApplicationRole($Name, $Description) {
$appRole = New-Object Microsoft.Open.AzureAD.Model.AppRole
$appRole.AllowedMemberTypes = New-Object System.Collections.Generic.List[string]
$appRole.AllowedMemberTypes.Add("Application");
$appRole.DisplayName = $Name
$appRole.Id = New-Guid
$appRole.IsEnabled = $true
$appRole.Description = $Description
$appRole.Value = $Name;
return $appRole
}
Function GenerateAppKey ($fromDate, $durationInYears, $pw) {
$endDate = $fromDate.AddYears($durationInYears)
$keyId = (New-Guid).ToString();
$key = New-Object Microsoft.Open.AzureAD.Model.PasswordCredential($null, $endDate, $keyId, $fromDate, $pw)
return $key
}
Function CreateAppKey($fromDate, $durationInYears, $pw) {
$testKey = GenerateAppKey -fromDate $fromDate -durationInYears $durationInYears -pw $pw
while ($testKey.Value -match "+" -or $testKey.Value -match "/") {
Write-Host "Secret contains + or / and may not authenticate correctly. Regenerating..." -ForegroundColor Yellow
$pw = ComputePassword
$testKey = GenerateAppKey -fromDate $fromDate -durationInYears $durationInYears -pw $pw
}
Write-Host "Secret doesn't contain + or /. Continuing..." -ForegroundColor Green
$key = $testKey
return $key
}
Function ComputePassword {
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
$aesManaged.GenerateKey()
return [System.Convert]::ToBase64String($aesManaged.Key)
}
$existingapp = $null
$existingapp = get-azureadapplication -SearchString $AppName
if ($existingapp) {
Remove-Azureadapplication -ObjectId $existingApp.objectId
}
Write-Host "Creating application for: $((Get-AzureADTenantDetail).displayName)"
$reqSP = $null
$reqSP = Get-AzureADServicePrincipal -SearchString "Office 365 Management APIs"
if (!$reqSP) {
$reqSP = Get-AzureADServicePrincipal -SearchString "OfficeManagePlatform"
}
if (!$reqSP) {
Login-AzureRmAccount -TenantId $customer.customercontextid -Credential $credentials
New-AzureRmADServicePrincipal -ApplicationId "5393580-f805-4401-95e8-94b7a6ef2fc2"
$reqSP = Get-AzureADServicePrincipal -SearchString "Office 365 Management APIs"
}
if ($reqSP) {
#$user = Get-AzureADUser -SearchString "gcitsreports"
$homePage = "https://secure.gcits.com"
$appIdURI = "https://secure.gcits.com/$((New-Guid).tostring())"
$logoutURI = "https://portal.office.com"
# Add Required Resources Access (Office 365 Management API)
$requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]
$microsoftGraphRequiredPermissions = GetRequiredPermissions -reqSP $reqSP -requiredApplicationPermissions "ServiceHealth.Read"
$requiredResourcesAccess.Add($microsoftGraphRequiredPermissions)
# Get an application key
$pw = ComputePassword
$fromDate = [System.DateTime]::Now
$appkey = CreateAppKey -fromDate $fromDate -durationInYears 2 $pw
Write-Host "Creating the AAD application ($appName) and accessing Microsoft.Graph" -ForegroundColor Blue
$aadApplication = New-AzureADApplication -DisplayName $appName `
-HomePage $homePage `
-ReplyUrls $homePage `
-IdentifierUris $appIdURI `
-LogoutUrl $logoutURI `
-PasswordCredentials $appKey `
-RequiredResourceAccess $requiredResourcesAccess `
-AvailableToOtherTenants $true
$servicePrincipal = New-AzureADServicePrincipal -AppId $aadApplication.AppId
Write-Host "Assigning Permissions" -ForegroundColor Yellow
foreach ($resource in $requiredResourcesAccess.ResourceAccess) {
if ($resource.Type -match "Role") {
New-AzureADServiceAppRoleAssignment -ObjectId $serviceprincipal.ObjectId `
-PrincipalId $serviceprincipal.ObjectId -ResourceId $reqSP.ObjectId -Id $resource.Id
}
}
# This provides the application with access to your customer tenants.
$group = Get-AzureADGroup -Filter "displayName eq 'Adminagents'"
Add-AzureADGroupMember -ObjectId $group.ObjectId -RefObjectId $servicePrincipal.ObjectId
$contracts = Get-AzureADContract -All $true
Write-Host "App Created" -ForegroundColor Green
# Define parameters for access token retrieval
$client_id = $aadApplication.AppId
$client_secret = $appkey.Value
foreach ($contract in $contracts) {
$tenant_id = $contract.customercontextid
$resource = "https://manage.office.com"
$authority = "https://login.microsoftonline.com/$tenant_id"
$tokenEndpointUri = "$authority/oauth2/token"
$content = "grant_type=client_credentials&client_id=$client_id&client_secret=$client_secret&resource=$resource"
# Try to execute the API call 6 times for each customer
$Stoploop = $false
[int]$Retrycount = "0"
do {
try {
$result = $null
$response = Invoke-RestMethod -Uri $tokenEndpointUri -Body $content -Method Post -UseBasicParsing
Write-Host "Retrieved Access Token for $($contract.displayname)" -ForegroundColor Green
# Assign access token
$access_token = $response.access_token
$headers = @{
Authorization = "Bearer $access_token"
PublisherIdentifier = $tenant_id
}
$testCall = "https://manage.office.com/api/v1.0/$tenant_id/ServiceComms/Messages"
$result = Invoke-RestMethod -Uri $testCall -Headers $headers
Write-Host "Retrieved Message Center Messages for $($contract.displayname)" -ForegroundColor Green
if ($result) {
Write-Host "Found result"
[array]$matchingMessages = $result.value | Where-Object {$_.Messages.messagetext -match $MatchingString}
if ($matchingMessages) {
foreach($matchingMessage in $matchingMessages){
$ActionDate = ([datetime]$matchingMessage.ActionRequiredByDate).ToLongDateString()
Write-Host "Matching message found for $($contract.DisplayName) with action required by $ActionDate" -ForegroundColor Blue
[PSCustomObject][ordered]@{
CustomerName = $contract.DisplayName
TenantId = $contract.CustomerContextId
MessageID = $matchingMessage.id
MessageTitle = $matchingMessage.Title
Message = ($matchingMessage.messages.messagetext -join "`n")
ActionRequiredByDate = $matchingMessage.ActionRequiredByDate
} | Export-csv C:tempMessageCenterDetails.csv -NoTypeInformation -Append
}
}
else {
Write-Host "No matching message found for $($contract.DisplayName)" -ForegroundColor Yellow
}
}
$Stoploop = $true
}
catch {
if ($Retrycount -gt 5) {
Write-Host "Could not get content after 6 retries."
$Stoploop = $true
}
else {
Write-Host "Could not get content. Retrying in 5 seconds..."
Start-Sleep -Seconds 5
$Retrycount ++
}
}
}
While ($Stoploop -eq $false)
}
Remove-AzureADApplication -ObjectId $aadApplication.objectId
}



