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:\temp\MessageCenterDetails.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:\temp\MessageCenterDetails.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 }
Leave a Reply
Want to join the discussion?Feel free to contribute!