Using PowerShell to talk to the Office 365 Service Communications API

MVP

I want to use PowerShell to export the most recent week's worth of Admin Message Center Messages to a spreadsheet because I want to annotate each item with what I am planning to do about each announcement. #Governance

 

I have an account for this purpose that is a global tenant admin and that does not use MFA.

 

I believe I need to use the O365 Service Communication API (Preview) as described at https://msdn.microsoft.com/office-365/office-365-service-communications-api-reference, in particular with this REST URI:  https://manage.office.com/api/v1.0/contoso.com/ServiceComms/Messages

 

In PowerShell, there is the Invoke-RestMethod command.

I'm passing the above URI (except with contoso.com replaced) for the value of the -URI parameter.

However, for authentication, passing a Credential doesn't work.  I need to create an Authorization Header with a value of Bearer and an AAD_Bearer_JWT_Token.

I'm lost when it comes to creating that. 

 

Do I really need to set up an Application listing in Azure Ad for my PowerShell script, per https://msdn.microsoft.com/en-us/office-365/get-started-with-office-365-management-apis ?

If so, beside the UI of the management portal having been redone, I don't see the Service Communication APIs being listed as an option when I am supposed to grant the application permission to access them.

 

Can anyone shed some light on this?

@paulschaeflein @Dean Gross @Anna Chu @Todd Klindt @Anne Michels @Peter Walke

 

 

 

 

 

19 Replies

Vasil, thanks for pointing me to that.  One thing that concerns me about that example is that it is using the old version of the API.  Note the command to get the cookie:

$cookie = (invoke-restmethod -contenttype "application/json" -method Post -uri "https://api.admin.microsoftonline.com/shdtenantcommunications.svc/Register" -body $jsonPayload).RegistrationCookie

It uses the old URI.  The documentation for the old API is at https://msdn.microsoft.com/en-us/library/office/dn776043(v=office.15).aspx and says "A new version of the Office 365 Service Communications API has been released in preview mode. It is recommended that new applications be built using this version. For more information, see Office 365 Service Communications API reference (preview)."

 

How would I do the registration step with the new API?  I don't see a Register method listed for the new API.

Michael

Hi Michael,

The v2 API (preview) requires that you register the script/app.  One immediate term option is to use the non-preview v1 API.  Sample code may be found here (download the zip file and look inside the Word document).  Also follow along with this blog series for additional code samples that are forthcoming.

Thanks,

Anne

Hi @Michael Blumenthal

 

I wrote the article that was originally referenced. It hasn't been updated for sometime. 

The new Service Communications API appears to be in preview at this stage, but it's probably a good idea for me to get my article updated, i'll endevour to do that this week some time.

 

All the information on the new API appears here: https://msdn.microsoft.com/office-365/office-365-service-communications-api-reference

 

I would probably suggest using MSAL libraries this time around to simplify the obtainment of a token.

@Vasil Michev, @Anne Michels, @Cam Murray Thank you all. 

 

First, I was able to use Cam's script to produe a spreadsheet.  My PowerShell looks like this:

$cred = get-credential
$jsonPayload = (@{userName=$cred.username;password=$cred.GetNetworkCredential().password;} | convertto-json).tostring()
$cookie = (invoke-restmethod -contenttype "application/json" -method Post -uri "https://api.admin.microsoftonline.com/shdtenantcommunications.svc/Register" -body $jsonPayload).RegistrationCookie
$jsonPayload = (@{lastCookie=$cookie;locale="en-US";preferredEventTypes=@(2)} | convertto-json).tostring()
$events = (invoke-restmethod -contenttype "application/json" -method Post -uri "https://api.admin.microsoftonline.com/shdtenantcommunications.svc/GetEvents" -body $jsonPayload)
$events.Events | export-csv -LiteralPath "C:\temp\2017-04-03-Messages.csv" -NoTypeInformation

It works.  It still needs some tweaking because one of the columns, with the message details, comes up containing objects.  But it's good enough for this week.  I'll improve on it next week when it is time for me to review the message center messages again.  What I did after I produced the CSV is save it as an XLSX and format the data as a table. I deleted a few empty or not helpful columns and re-ordered the rest.  I then added two columns, "Governance Assessment" and "Notes".  I then filled in those columns as needed, documenting which Messages require further work on my part.  For example, some changes "user will just figure out". Others might be something I have to discuss with a larger audience, such as the SharePoint Governane Team, the other O365 Admins, or Corporate Communications.   In other words, I've solved my business need for this week, but incurred technical debt to do so.

 

Looking ahead, I want to use the new APIs.  This means I will have to create an app registration in Azure AD.  @Anne Michels, are there newer instructions than https://msdn.microsoft.com/en-us/office-365/get-started-with-office-365-management-apis that show how to do that registration in the new Azure Admin Portal UI?  Since this is a script that is calling the APIs rather than a web app, guidance on property values like Sign-On URL and App ID UI and Reply URL would be helpful. 

Also, what permissions should it be granted if it just needs to read Message Center messages?  I did not see a permission that provides that access.

 

@Cam Murray it would be great if you could update your article to use the new API.  As you can see from above, guidance on app registration in Azure AD is needed too.  Do you know of any PowerShell examples for using the MSAL to get the token? Are the MSAL libraries something I need to install locally before I can reference them and use them in PowerShell?  Where do I get them from?

 

Thanks,

Michael

Attached is the Spreadsheet I created based on the messages I retrieved via the script.  I think this is something companies will want to do weekly as part of their Office 365 governance process.

Here's an improved version of the PowerShell.  It still uses the old API however. This version is better because it includes the message body and adds columns for GovernanceResponse and Notes to the CSV.  You still have to open it in Excel and format it as a table. 

 

$cred = get-credential
$jsonPayload = (@{userName=$cred.username;password=$cred.GetNetworkCredential().password;} | convertto-json).tostring()
$cookie = (invoke-restmethod -contenttype "application/json" -method Post -uri "https://api.admin.microsoftonline.com/shdtenantcommunications.svc/Register" -body $jsonPayload).RegistrationCookie
$jsonPayload = (@{lastCookie=$cookie;locale="en-US";preferredEventTypes=@(2)} | convertto-json).tostring()
$events = (invoke-restmethod -contenttype "application/json" -method Post -uri "https://api.admin.microsoftonline.com/shdtenantcommunications.svc/GetEvents" -body $jsonPayload)
$outfileName = "C:\temp\Message Center\" + $(get-date).ToString("yyyy-MM-dd") + "-Messages.csv"


$FormatedEvents = $events.Events | foreach {
                $row = new-object PSObject
                add-member -InputObject $row -MemberType NoteProperty -Name MessageID -Value $_.Id
                add-member -InputObject $row -MemberType NoteProperty -Name LastUpdatedTime -Value $_.LastUpdatedTime.toString("MM/dd/yyyy h:mm tt")
                add-member -InputObject $row -MemberType NoteProperty -Name Urgency -Value $_.UrgencyLevel
                add-member -InputObject $row -MemberType NoteProperty -Name ActionType -Value $_.ActionType
                add-member -InputObject $row -MemberType NoteProperty -Name Category -Value $_.Category
                add-member -InputObject $row -MemberType NoteProperty -Name Status -Value $_.Status  #is this always null?
                if ($_.ActionRequiredByDate -eq $null){
                    add-member -InputObject $row -MemberType NoteProperty -Name ActionRequiredBy -Value ""
                    } else {
                    add-member -InputObject $row -MemberType NoteProperty -Name ActionRequiredBy -Value $_.ActionRequiredByDate.toString("MM/dd/yyyy h:mm tt")
                }

                add-member -InputObject $row -MemberType NoteProperty -Name Title -Value $_.Title
                add-member -InputObject $row -MemberType NoteProperty -Name Message -Value $_.Messages[0].MessageText
                add-member -InputObject $row -MemberType NoteProperty -Name AdditionalInfo -Value $_.ExternalLink
                add-member -InputObject $row -MemberType NoteProperty -Name GovernanceResponse -Value ""
                add-member -InputObject $row -MemberType NoteProperty -Name Notes -Value ""

                Write-Output $row
}
$FormatedEvents| export-csv -LiteralPath $outfileName -NoTypeInformation

 

Hello @Michael Blumenthal, many thanks for sharing your script with us. Do you have any news about the new API ?

Hello Anne,

 

I'm using powershell to contact the service communication API's (connecting via the azure app)

Now i am able to get data from the API's but i want to set a filter that returns only data from a specific time. Do you have an idea how this filter should be built?

Get Messages

Description:

Returns the messages about the service over a certain time range. Use the type filter to filter for "Service Incident", "Planned Maintenance", or "Message Center" messages.

  Service Description
Path/Messages 
FiltersStartTime
Filter by Start Time (DateTimeOffset, default: ge CurrentTime - 7 days).
   

 

I'm trying to create this uri with above filter: 

"https://manage.office.com/api/v1.0/$tenant/ServiceComms/Messages?$filter=StartTime ge 'CurrentTime - 2 days'"

 

Source:

https://msdn.microsoft.com/en-us/office-365/office-365-service-communications-api-reference

 

Thanks in advance!

Brian

 

@Brian Teerlynckdid you ever figure out how to get this type of filter to work? or how to get just the messages from the Message Center?

@Michael Blumenthalhave you ever figured out how to use the new API to get the Message Center messages? this has me stumped.

I ended up just subscribing to the message center weekly digest and using that as I describe in my blog. https://michaelblumenthal.me/2017/07/office-365-governance-essentials-step-1-review-annotate-and-sha...

You can configure your weekly digest subscription in your Message Center options.

Thanks, I have been able to create a Flow that gets the messages, but the filtering does not work (appears to be a bug in either the API or in Flows implementation) so I still get the Incidents, which is not optimal. 

I am mystified why something that should be so simple, is not. 

Can you share how you set that Flow up? What trigger and actions did you use?

If the Flow action brings back all messages and incidents, can you use a conditional (aka If statement) to branch on if it is a message or an incident?

Cam, did you ever figure out how to leverage the new V2 APIs with PowerShell?   The V1 APIs are a lot easier to use, but the V2 APIs look like they would offer more capabilities.   Also, the V2 APIs have been in preview for over a year and a half, so I wonder how much longer the V1 APIs will be supported.  

I created a script to query the old API (before I knew there was a new one).  It works for the most part.  I send 22 variables back to my RMM (N-Central) to use as an alerting tool.  I haven't turned alerting on for it yet because I've still been testing it for the most part.  The one thing I have noticed is that sometimes the messagetext does not always show what it should.  It should show messagetext[0] which would correspond to the last message posted for the service, but sometimes it get skewed.  I'm leaning towards this being an anomaly in the method Microsoft uses to post the messages.  I've spent all night tonight (8+ hours) and an equal amount of time other nights trying to figure out how to port this to the new API but I keep coming up short.  So far the closest I have come is by using the Exch-REST module and using the Get-EXRMSubscriptionContentBlob cmdlet.  See below.

 

$tenantGUID = "YOURTENANTID"

$test = Get-EXRMSubscriptionContentBlob -ContentURI "https://manage.office.com/api/v1.0/$tenantGUID/ServiceComms/Messages"
# This will net you almost what $Events will from the O365ServiceCommunications module

$test.value | ? {$_.WorkloadDisplayName -ne $null} | FT ID,WorkloadDisplayName,StartTime,EndTime,LastUpdatedTime,Status
$test.value.messages.messagetext

 

Here is the code for the script for the previous API.  Let's keep this thread going.  I'd really like to crack the mystery of API v2.  :)

 

PS: To get the pertinent data from the script you'll want to return $service and $servicemsg for each of the services - these are the variables I am sending back to my RMM.

 

Cheers,

Josh

 

PSS: Here are some notes on how to create the Azure App and obtain the oAuth token.

 

https://docs.microsoft.com/en-us/office/office-365-management-api/

Follow instructions here to create Azure App for Certificate Secret key: https://docs.microsoft.com/en-us/office/office-365-management-api/get-started-with-office-365-manage...

Here's a video outlining some of the process: https://www.youtube.com/watch?v=WygwzN9FfMQ

 

# Create certificate, then Export .cer via MMC > Certificates Snap-In > My User Account

$cert = New-SelfSignedCertificate -Type CodeSigningCert -KeySpec Signature `
-Subject "E=my@email.com,CN=My Name" -KeyExportPolicy Exportable `
-HashAlgorithm sha256 -KeyLength 2048 -NotAfter ((Get-Date).AddYears(10)) `
-FriendlyName "Company Name Office 365 Service Communications API" `
-CertStoreLocation "Cert:\CurrentUser\My" -KeyUsageProperty Sign -KeyUsage CertSign

 

# Get values to create oAuth token
$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import("C:\Users\xxxx\xxxx\O365CommAPI.cer")
$bin = $cer.GetRawCertData()
$base64Value = [System.Convert]::ToBase64String($bin)
$bin = $cer.GetCertHash()
$base64Thumbprint = [System.Convert]::ToBase64String($bin)
$keyid = [System.Guid]::NewGuid().ToString()

Error updating manifest?:
"Failed to save manifest. Error details: KeyValueMustBeNull"
https://social.msdn.microsoft.com/Forums/en-US/0e88e784-5e5b-496f-b554-0935ab34b440/cannot-update-ke...
"because the key is already stored. If you go to "Settings -> Keys" and remove the public key stored here it is possible to update it again."

# Create app of type Web app / API in Azure AD, generate a Client Secret, and update the client id and client secret here
$ClientID = "xxxx"
$ClientSecret = "xxxx"
$loginURL = "https://login.microsoftonline.com/"
$tenantdomain = "xxxx"
# Get the tenant GUID from Properties | Directory ID under the Azure Active Directory section
# https://docs.microsoft.com/en-us/onedrive/find-your-office-365-tenant-id
$TenantGUID = "xxxx"
$resource = "https://manage.office.com"
# auth
$body = @{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}

 

I'm not exactly sure what to do once you have the token, but this is where I left off - also using the Exch-REST module I was able to somewhat replicate the data obtained from the O365ServiceCommunications module.  Was anyone able to make it any further?

Hello Everyone

 

I'm new to the community. But if you are still looking for something production ready. I just pushed a solution for this to my Git repo. You can find it here https://github.com/KDebugMan/O365ServiceHealthStatusAlert

 

Open for feedback and comments.

 

Cheers,

Khan