Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Example how to fetch O365 Service Health and O365 Service Messages using the public REST APIs
function New-ISO8601DateTimeFromSystemDateTime
{
[cmdletbinding()]
param
(
[Parameter(Mandatory=$true)][DateTime]$Date
)
$sb = New-Object System.Text.StringBuilder
$sb.Append($Date.Year.ToString("0000")) | Out-Null
$sb.Append("-") | Out-Null
$sb.Append($Date.Month.ToString("00")) | Out-Null
$sb.Append("-") | Out-Null
$sb.Append($Date.Day.ToString("00")) | Out-Null
$sb.Append("T") | Out-Null
$sb.Append($Date.Hour.ToString("00")) | Out-Null
$sb.Append(":") | Out-Null
$sb.Append($Date.Minute.ToString("00")) | Out-Null
$sb.Append(":") | Out-Null
$sb.Append($Date.Second.ToString("00")) | Out-Null
$sb.Append("Z") | Out-Null
$sb.ToString()
}
function Get-AccessToken
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$ClientId,
[Parameter(Mandatory=$true)][string]$ClientSecret,
[Parameter(Mandatory=$true)][string]$TenantId,
[Parameter(Mandatory=$true)][string]$Resource
)
$body = @{
"grant_type" = "client_credentials"
"resource" = $Resource
"client_id" = $ClientId
"client_secret" = $ClientSecret
}
Invoke-RestMethod -Uri https://login.microsoftonline.com/$TenantId/oauth2/token?api-version=1.0 -Method Post -Body $body | SELECT -ExpandProperty access_token
}
function Get-AuthenticationHeaders
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$AccessToken
)
begin
{
}
process
{
@{
'Content-Type' = 'application/json'
'Authorization' = "Bearer $($AccessToken)"
}
}
end
{
}
}
function Get-ServiceHealth
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$AccessToken,
[Parameter(Mandatory=$true)][string]$Tenant,
[Parameter(Mandatory=$false)][switch]$IncludeFeatureStatus
)
begin
{
$headers = Get-AuthenticationHeaders -AccessToken $AccessToken
$uri = "https://manage.office.com/api/v1.0/$tenant/ServiceComms/CurrentStatus"
$results = @()
}
process
{
$json = Invoke-RestMethod -Uri $uri –Headers $headers –Method GET
foreach( $service in $json.value )
{
$status = [PSCustomObject]@{
WorkloadDisplayName = $service.WorkloadDisplayName
Workload = $service.Workload
WorkloadStatus = $service.StatusDisplayName
StatusTime = $service.StatusTime
IncidentIds = $service.IncidentIds -join ", "
}
if( $IncludeFeatureStatus )
{
$status | Add-Member -MemberType NoteProperty -Name FeatureStatus -Value @()
foreach( $featureStatus in $service.FeatureStatus )
{
$status.FeatureStatus += [PSCustomObject] @{
FeatureName = $featureStatus.FeatureDisplayName
FeatureStatus = $featureStatus.FeatureServiceStatusDisplayName
}
}
}
$results += $status
}
}
end
{
$results
}
}
function Get-ServiceHealthMessages
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$AccessToken,
[Parameter(Mandatory=$true)][string]$Tenant,
[Parameter(Mandatory=$false)][DateTime]$StartDate = $(Get-Date).AddDays(-7),
[Parameter(Mandatory=$false)][DateTime]$EndDate,
[Parameter(Mandatory=$false)][string]$Workload,
[Parameter(Mandatory=$false)][ValidateSet("MessageCenter","Incident")] [string]$MessageType
)
begin
{
$messages = @()
$filters = @()
$headers = Get-AuthenticationHeaders -AccessToken $AccessToken
$uri = "https://manage.office.com/api/v1.0/$tenant/ServiceComms/Messages"
}
process
{
# add StartDate filter
if( $StartDate )
{
$formattedStartDate = New-ISO8601DateTimeFromSystemDateTime -Date $StartDate.ToUniversalTime()
$filters += "StartTime ge $formattedStartDate"
}
# add EndDate filter (excludes anything w/o and end date)
if( $EndDate )
{
$formattedEndDate = New-ISO8601DateTimeFromSystemDateTime -Date $StartDate.ToUniversalTime()
$filters += "EndTime le $formattedEndDate"
}
# add Workload filter
if( $Workload )
{
$filters += "Workload eq '$Workload'"
}
# add MessageType filter
if( $MessageType )
{
$filters += "MessageType eq Microsoft.Office365ServiceComms.ExposedContracts.MessageType'$MessageType'"
}
# udpate the URL with the filter
if( $filters.Count -ge 0 )
{
$uri = "$uri`?`$filter= $($filters -join ' and ')"
}
$json = Invoke-RestMethod -Uri $uri –Headers $headers –Method GET
if( $json -and $json.value )
{
foreach( $message in $json.value )
{
$messages += [PSCustomObject] @{
ID = $message.Id
StartTime = $message.StartTime
EndTime = $message.EndTime
Status = $message.Status
LastUpdatedTime = $message.LastUpdatedTime
Workload = $message.Workload
WorkloadDisplayName = $message.WorkloadDisplayName
Feature = $message.Feature
FeatureDisplayName = $message.FeatureDisplayName
AffectedUserCount = $message.AffectedUserCount
AffectedTenantCount = $message.AffectedTenantCount
Title = $message.Title
Severity = $message.Severity
MessageType = $message.MessageType
Classification = $message.Classification
ImpactDescription = $messgae.ImpactDescription
PostIncidentDocumentUrl = $message.PostIncidentDocumentUrl
AffectedWorkloadNames = $message.AffectedWorkloadNames -join ", "
AffectedWorkloadDisplayNames = $message.AffectedWorkloadDisplayNames -join ", "
LatestMessage = $($message.Messages | SORT PublishedTime | SELECT -First 1 | % { $_.MessageText })
}
}
}
}
end
{
$messages
}
}
# required Application permissions > Office 365 Management APIs > ServiceHealth.Read
$tenant = "contoso.onmicrosoft.com"
$clientId = "565a7e3a-1234-1234-1234-9ed1efc4bb70" # aka "Application ID" in Azure Portal > Azure Active Directory > App Registrations
$clientSecret = 'C/xxxxx-.xxxx+[vJ_o:VWs7.xxxxxx' # aka "Keys" in Azure Portal > Azure Active Directory > App Registrations
$accessToken = Get-Token -Tenant $tenant -ClientID $clientId -ClientSecret $clientSecret -Resource "https://manage.office.com"
if( $accessToken )
{
# get all messages and save to csv
$messages = Get-ServiceHealthMessages -AccessToken $accessToken.AccessToken -Tenant $tenant -StartDate $(Get-Date).AddDays(-30) -MessageType MessageCenter
$messages | Export-Csv -Path "C:\_temp\Messages.csv" -NoTypeInformation
# get all service status' and save to csv
$serviceStatus = Get-ServiceHealth -AccessToken $accessToken.AccessToken -Tenant $tenant -IncludeFeatureStatus
$summary = $serviceStatus | SELECT WorkloadDisplayName, WorkloadStatus, @{ n="FeatureStatus"; e={$_.FeatureStatus | SELECT FeatureName, FeatureStatus}} | SELECT -Property * -ExpandProperty FeatureStatus -ExcludeProperty FeatureStatus | SORT WorkloadDisplayName, WorkloadStatus | SELECT WorkloadDisplayName, WorkloadStatus, FeatureName, FeatureStatus
$summary | Export-Csv -Path "C:\_temp\ServiceStatus.csv" -NoTypeInformation
# if any have "service degradation" send an email
if( $degradedItems = $summary | ? { $_.WorkloadStatus -eq "Service degradation" } )
{
$head = "
<style>
body {
font-family:Monospace;
font-size:10pt;
}
td, th {
border:0px solid black;
border-collapse:collapse;
white-space:pre;
}
th {
color:white;
background-color:black;
}
table, tr, td, th {
padding: 5px;
margin: 0px;
white-space:pre;
}
tr:nth-child(odd) {
background-color: lightgray
}
table {
margin-left:5px;
margin-bottom:20px;
}
h2 {
font-family:Tahoma;
color:#6D7B8D;
}
</style>"
# covert to HTML table
$html = $degradedItems | SELECT WorkloadDisplayName, WorkloadStatus, FeatureName, FeatureStatus | ConvertTo-Html -As Table -Head $head
# send the email
Send-MailMessage -Body $html -BodyAsHtml -Subject "O365 Service Degradation" -To "to@contoso.com" -From "from@contoso.com" -SmtpServer "smtp.contoso.com"
}
}
@bzabber
Copy link

bzabber commented Oct 16, 2020

Does your $message.PostIncidentDocumentURL provide you with the URL or is it always blank? When I try to include it, it's always blank.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment