Forum Discussion
How to use Office 365 Service Communications API V2 with PowerShell
We are using the O365ServiceCommunications module written by Matt McNabb (https://github.com/mattmcnabb/O365ServiceCommunications) and started receiving "The service is unavailable" messages on 9/15. We didn't realize the API that this module utilizes was slated to be deprecated. We would also be interested if anyone has any code to share on how to retrieve health and message center events using v2 of the API.
- June CastilloteSep 17, 2018Copper Contributorfigured it out, after hours of testing. really lacking documentation.
I find that the v2 API filter doesn't work, as it returns all messages to your rest query. Although this is no big deal since the results can be filtered for consumption anyway.
I'm still working on the script, and would probably be sharing tomorrow.- Ankurkumar T PatelOct 25, 2018Copper Contributor
could you please share script with API v2. It's not working for us.
- Shawn BeckersOct 25, 2018Brass Contributor
Below is the PowerShell script I came up with which uses v2 of the Graph API. A few notes:
- You will need to register an Azure app for use with this script. The app will need to be granted permissions to read tenant health info. I have not included instructions for registering the Azure app. A good starting point on how to do this can be found at the following URL: https://lazyadmin.nl/it/using-microsoft-graph-api-with-powershell/.
- Review the "script variables" section of the script and adjust as necessary.
- As this is my first try at using the Graph API, I'm not convinced the portion of the code which retrieves the OAuth token is optimal. Please share any changes you make to improve that portion of the script.
- The secret for the registered app is stored in a file (location specified in PassFile variable). For the version of the script we use, we encrypt PassFile and have the script decrypt it in order to retrieve the secret. For the version I've attached I assume an unencrypted file is used to store the secret.
Hopefully others can make use of this and please share any improvements you make with the community.
--------------------------
Monitor-Tenant-Health
--------------------------
#Script variables
$HistoryFile = "Enter folder and path to file which will be used to track tenant health history (i.e. C:\Temp\TenantHealth.csv)"
$PassFile = "Location of file containing client secret for Azure app created for use in obtaining tenant health"
$ClientID = "Application ID of Azure app created for use in obtaining tenant health"
$TenantDomain = "yourtenant.onmicrosoft.com"
$TenantGUID = "GUID of your tenant"
$NotificationRecipients = @("someone@somewhere.com")
$NotificationSender = "donotreply@somewhere.com"
$SmtpServer = "smtpserver.somewhere.com"
$SmtpPort = "25"
$RedirectUri = "https://localhost"
$LoginUrl = "https://login.microsoft.com"
$Resource = "https://manage.office.com"#Retrieve health events from previous run
$HistoryTable = @{}
If (Test-Path $HistoryFile)
{
$HistoryFileContents = Get-Content -Path $HistoryFile
ForEach ($Entry in $HistoryFileContents) {$HistoryTable.Add(($Entry.Split(","))[0],($Entry.Split(","))[1])}
}#Retrieve OAuth token
$ClientSecret = Get-Content $PassFile
$RequestBody = @{grant_type="client_credentials";redirect_uri=$RedirectUri;resource=$Resource;client_id=$ClientID;client_secret=$ClientSecret}
$OAuth = Invoke-RestMethod -Method Post -Uri $LoginUrl/$TenantDomain/oauth2/token?api-version=1.0 -Body $RequestBody
$HeaderParams = @{'Authorization'="$($OAuth.Token_Type) $($Oauth.Access_Token)"}#Retrieve current O365 health events posted for tenant
$O365Incidents = (Invoke-RestMethod -Method Get -Uri $Resource/api/v1.0/$TenantGUID/ServiceComms/Messages -Headers $HeaderParams).Value | Where {$_.MessageType -eq "Incident"}#Get list of new or changed events
$NewChangedEvents = @()
ForEach ($Incident in $O365Incidents)
{
$NewChangedFlag = 0
If ($HistoryTable.ContainsKey($Incident.Id))
{
If ($HistoryTable[$Incident.Id] -ne (Get-Date($Incident.LastUpdatedTime)).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss")) {$NewChangedFlag = 1; $HistoryTable[$Incident.Id] = (Get-Date($Incident.LastUpdatedTime)).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss")}
}
Else
{
$HistoryTable.Add($Incident.Id,(Get-Date($Incident.LastUpdatedTime)).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss"))
$NewChangedFlag = 1
}
If ($NewChangedFlag -eq 1)
{
$LatestMessage = $($Incident.Messages | Sort-Object -Property PublishedTime)[$Incide3nt.Messages.Count - 1]
$Event = New-Object PSObject
$Event | Add-Member -Type NoteProperty -Name ID -Value $Incident.Id
$Event | Add-Member -Type NoteProperty -Name Service -Value $Incident.WorkloadDisplayName
$Event | Add-Member -Type NoteProperty -Name Status -Value $Incident.Status
$Event | Add-Member -Type NoteProperty -Name StartTime -Value (Get-Date($Incident.StartTime)).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss")
$Event | Add-Member -Type NoteProperty -Name LastUpdatedTime -Value (Get-Date($Incident.LastUpdatedTime)).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss")
$Event | Add-Member -Type NoteProperty -Name Details -Value $LatestMessage.MessageText
$Event | Add-Member -Type NoteProperty -Name NotificationRecipients $NotificationRecipients
$NewChangedEvents += @($Event)
}
}ForEach ($Event in $NewChangedEvents)
{
#Build HTML for email
$MessageText = $Event.Details -replace("`n",'<br>') -replace([char]8217,"'") -replace([char]8220,'"') -replace([char]8221,'"') -replace('\[','<b><i>') -replace('\]','</i></b>')
$HTML = "<html>"
$HTML += "<style>"
$HTML += "BODY{font-family: Arial; font-size: 10pt;}"
$HTML += "H1{font-size: 22px;}"
$HTML += "H2{font-size: 18px; padding-top: 10px;}"
$HTML += "H3{font-size: 16px; padding-top: 8px;}"
$HTML += "H4{font-size: 12px; padding-top: 4px;}"
$HTML += "TABLE{border: 1px solid black; border-collapse: collapse; font-size: 8pt; table-layout: fixed; width: 800px;}"
$HTML += "TABLE.summary{text-align: center; width: auto;}"
$HTML += "TH{border: 1px solid black; background: #dddddd; padding: 5px; color: #000000;}"
$HTML += "TD{border: 1px solid black; padding: 5px; vertical-align: top; }"
$HTML += "td.pass{background: #7FFF00;}"
$HTML += "td.warn{background: #FFE600;}"
$HTML += "td.fail{background: #FF0000; color: #ffffff;}"
$HTML += "td.info{background: #85D4FF;}"
$HTML += "ul{list-style: inside; padding-left: 0px;}"
$HTML += ".firstrunnotice { font-size: 14px; color: #4286f4; }"
$HTML += "</style>"
$HTML += "<body>"
$HTML += "<table>"
$HTML += "<tr bgcolor=""#000099"">"
$HTML += " <td align=""center""><font color=""#ffffff"">Event ID</font></td>"
$HTML += " <td align=""center""><font color=""#ffffff"">Service</font></td>"
$HTML += " <td align=""center""><font color=""#ffffff"">Status</font></td>"
$HTML += " <td align=""center""><font color=""#ffffff"">Event Start Time</font></td>"
$HTML += " <td align=""center""><font color=""#ffffff"">Last Updated</font></td>"
$HTML += "</tr>"
$HTML += "<tr bgcolor=""#0000FF"">"
$HTML += " <td align=""center""><font color=""#ffffff"">$($Event.ID)</font></td>"
$HTML += " <td align=""center""><font color=""#ffffff"">$($Event.Service)</font></td>"
$HTML += " <td align=""center""><font color=""#ffffff"">$($Event.Status)</font></td>"
$HTML += " <td align=""center""><font color=""#ffffff"">$($Event.StartTime)</font></td>"
$HTML += " <td align=""center""><font color=""#ffffff"">$($Event.LastUpdatedTime)</font></td>"
$HTML += "<tr><td colspan=""5"" style=""border:none""> </td></tr>"
$HTML += "<tr>"
$HTML += " <td colspan=""5"">$MessageText<br/> <br/><a href=""http://o365health.ad.csbsju.edu/"">Office 365 Health Dashboard</a></td>"
$HTML += "</tr>"
$HTML += "<tr><td colspan=""5"" style=""border:none""> </td></tr>"
$HTML += "</table>"
$HTML += "</body>"
$HTML += "</html>"#Send notification
Send-MailMessage -From $NotificationSender -To $NotificationRecipients -Subject "O365 Health Alert - $($Event.Service) [Event ID: $($Event.ID)]" -Body $HTML -BodyAsHtml -SmtpServer $SmtpServer -Port $SmtpPort -UseSsl
}
- Shawn BeckersSep 17, 2018Brass Contributor
FYI - It seems like the older API endpoint is back up again. At least for us it's working. However, we do plan to work on converting our scripts to using the newer API. Biggest hurdle for us was just registering the app in Azure and figuring out the best way to manage the authentication tokens. The documentation is definitely lacking, but the objects returned from the API seem somewhat similar to those of the older API.