Skip to main content

Using Message and Adaptive Cards with Powershell in Email and Microsoft Teams in Office365

Cards are "a user-interface (UI) container for short or related pieces of information ref" which there are currently two formats for, MessageCards and AdativeCards . The underlying data format is a JSON object that describes the content to be rendered by a supporting client. Eg here is a basic sample


So they are a simple way of presenting information in consistent manner which  then makes deploying that same UI experience across multiple client forms factors (mobile,web,desktop) a lot easier.  Cards also allow you to define actions, so then the cards themselves can become a small app or part of an integration piece of a Line of Business workflow etc. Actionable Cards in Outlook have the ability to link with an Outlook Addin's see which starts to really extend what you can use cards for. This means your no longer just stuck in the limited feature set of Cards but you can use the richness available in the Outlook Addin framework.

Email Security  

Cards rely on JSON (JavaScript Object Notation) and JavaScript to provide the functionality that they do. Messaging people that have been around for a while would know that JavaScript (and other active Content) in Email bodies hasn't been allowed for a very long time because of the risk of malicious code execution. Therefor senders of Actionable cards must be registered with Microsoft and Approved using the following portal . If you just want to send Actionable messages within your own tenant you would registered your App within your tenant but your Actionable Messages would not have any context outside of your tenant (or other peoples Actionable Messages within your own).

Client Support 

 If you are a student of Technology cards are a good study into cloud cadences and just how quickly standards can come and go now but also how hard it can be to get all your software wheels turning to keep pace. For example MessageCards are now considered legacy however as of this post June 2018 both the Outlook desktop  and the Team's clients don't support adaptive cards in their current general releases.  Eg for Adaptive Cards support in Teams you will need the developer preview and in the Outlook Desktop client you need to be on the Monthly Channel with version 1085. This is neither good nor bad but if you find Adaptive Cards the exact thing you need to solve your problem you should be aware of client support status before you start your planning. 

Examples

For this post I've created both a MessageCard PowerShell sample that can use be used in either Outlook or Teams and a Adaptive Cards sample that can be used primary in OWA at the moment. The to produce just the MessageCard can be found https://github.com/gscales/Powershell-Scripts/blob/master/CreateMessageCard.ps1 and the Adaptive Card https://github.com/gscales/Powershell-Scripts/blob/master/CreateAdaptiveCard.ps1

Microsoft Teams - In/Out - Out of Office Teams Message card

This script will produce the following output to a Teams Channel using a incoming WebHook to receive the Messages



This example uses my Exch-REST module available from the PowerShell Galleryhttps://www.powershellgallery.com/packages/Exch-Rest and GitHub https://github.com/gscales/Exch-Rest . This contains all the code to first get the Group associated with a particular team, get the members of that groups mail Addresses, make the Graph Mail tips request and then package the results as JSON message which can then be posted to a WebHook.

To Setup a WebHook for the script to post to on a Team see https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors#setting-up-a-custom-incoming-webhook


 So lets look at how this all fits together



Connect-EXRMailbox -mailbox mailbox@domain.com
$GroupName = "A Team" $Group = Get-EXRModernGroups -GroupName $GroupName $Members = Get-EXRGroupMembers -GroupId $Group.id
The above code first gets the ModernGroup which is associated with the Team you are working with using the Teams DisplayName. It then uses the GroupId that is returned to make a request to Get the Members of that group.



$OOFMailboxCol = @() $mtHash = @{} foreach($Member in $Members){     if(![String]::IsNullOrEmpty($Member.mail)){         $OOFMailboxCol += $Member.mail         $mtHash.Add($Member.mail,$Member.displayName)     }  }
This code creates a collection of Mailboxes to use the Get-EXRMailTips operation and also creates a hashtable to allow us to map the email address in the Mailtips response back to the DisplayName because by default MailTips only gives you back the EmailAddress.



$Mailtips = Get-EXRMailTips -Mailboxes $OOFMailboxCol -tips "automaticReplies"
$FactsColl = ($Mailtips | select @{Name='EmailAddress';Expression={$mtHash[$_.emailAddress.address]}},@{Name='OOF Message';Expression={$val =($_.automaticReplies.message -replace '<[^>]+>','').Trim();if([String]::IsNullOrEmpty($val)){"In"}else{"Out : " + $val}}})
$WebhookAddress = "https://outlook.office.com/webhook/cc1b7a"
Invoke-WebRequest -Uri $WebhookAddress -Method Post -ContentType 'Application/Json' -Body (New-EXRMessageCard -Facts $FactsColl -Summary "Team OOF Status" -Title "Team OOF Status")
The first line of the above code makes the MailTips request for the addresses that are passed into it. The next line builds a Collection of Facts which is a Messagecard element that display a two column table. I've used expressions to clean up the results as the MailTips response is a HTML value that needs to be formatted back to text for the card and I've added some In/Out prefixes based on the response. The last line of code is what submits the Message card to the configured webhook address in the $WebhookAddress variable.

The Full Sourcecode for this script can be found https://github.com/gscales/Exch-Rest/blob/master/Samples/SendOOFMessageCard.ps1


Adaptive Card to show the Office365 Service health status

Pre-requisites   

As I mentioned previously if your going to send an Adaptive Card as an email you need to be registered in the portal and include the originator Id you receive in your adaptive Card. For testing what you can do is send an Adaptive Card to yourself (from yourself) and without the originator property being set and it will appear okay. But if you where to send it to someone else the content will be stripped from the message.

Example

One of the other things that you can get from the manage.office.com endpoint that I talked about in this post is the Service Health information about each of the different workloads in Office365. In this example I use the Management API to produce the following adaptive card example eg



Adaptive Cards have a lot more formatting options for example you can have a column set and use different font colors. To make the script as dynamic as possible I do the color switching using a hashtable. This example uses my Exch-REST module available from the PowerShell Galleryhttps://www.powershellgallery.com/packages/Exch-Rest and GitHub https://github.com/gscales/Exch-Rest  . The script does the following


$To = "gscales@datarumble.com" Connect-EXRMailbox -MailboxName "gscales@datarumble.com" Connect-EXRManagementAPI -UserName "gscales@datarumble.com" $StatusArray = Get-EXRMCurrentStatus | Select-Object WorkLoad,Statusdisplayname,StatusTime 
In the above code it makes a connection to the Mailbox and ManagementAPI and then builds an collection for the service health information.


$ColorSwitchHash = @{} $ColorSwitchHash.Add("Service degradation","attention") $ColorSwitchHash.Add("Normal service","good")
This code sets up a HashTable that will be used to display a different color for the service status column based on its status. Note the Adaptive Cards support a subset of the MarkDown format so attention (red), good (green) etc.



$Card = New-EXRAdaptiveCard -Columns $StatusArray -ColorSwitchColumnNumber 1 -ColorSwitchHashTable $ColorSwitchHash -ColorSwitchDefault warning Send-EXRAdaptiveCard -To $To -Subject "Office 365 Service Status" -AdaptiveCard $Card
The last part of the code generates the AdatpiveCard, the ColorSwitchColumnNumber is a zero based array to match with the collection that is passed in. What Send-EXRAdaptiveCard does is wraps the JSON from the Card in the following script which is necessary when you send a message.


<script type="application/adaptivecard+json"></script>



Hire me - I'm currently available to help with any Exchange\Office365 related development work or scripting please contact me at gscales@msgdevelop.com (nothing too big or small). I'd also be interested in hearing from any companies that want to sponsor open source projects around Exchange and Office365 development.

Popular posts from this blog

Testing and Sending email via SMTP using Opportunistic TLS and oAuth in Office365 with PowerShell

As well as EWS and Remote PowerShell (RPS) other mail protocols POP3, IMAP and SMTP have had OAuth authentication enabled in Exchange Online (Official announcement here ). A while ago I created  this script that used Opportunistic TLS to perform a Telnet style test against a SMTP server using SMTP AUTH. Now that oAuth authentication has been enabled in office365 I've updated this script to be able to use oAuth instead of SMTP Auth to test against Office365. I've also included a function to actually send a Message. Token Acquisition  To Send a Mail using oAuth you first need to get an Access token from Azure AD there are plenty of ways of doing this in PowerShell. You could use a library like MSAL or ADAL (just google your favoured method) or use a library less approach which I've included with this script . Whatever way you do this you need to make sure that your application registration  https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia  https://en.wikipedia.org/wiki/Opportunistic_TLS .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th

The MailboxConcurrency limit and using Batching in the Microsoft Graph API

If your getting an error such as Application is over its MailboxConcurrency limit while using the Microsoft Graph API this post may help you understand why. Background   The Mailbox  concurrency limit when your using the Graph API is 4 as per https://docs.microsoft.com/en-us/graph/throttling#outlook-service-limits . This is evaluated for each app ID and mailbox combination so this means you can have different apps running under the same credentials and the poor behavior of one won't cause the other to be throttled. If you compared that to EWS you could have up to 27 concurrent connections but they are shared across all apps on a first come first served basis. Batching Batching in the Graph API is a way of combining multiple requests into a single HTTP request. Batching in the Exchange Mail API's EWS and MAPI has been around for a long time and its common, for email Apps to process large numbers of smaller items for a variety of reasons.  Batching in the Graph is limited to a m
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.