Thursday, May 30, 2019

Sending a Cloud Voice mail using the Microsoft Graph or EWS from Powershell

Back in February of this year Microsoft announced the retirement of Exchange UM services in favour of Cloud voicemail that has been around for a while. Both of these services use your Exchange mailbox to store voicemail messages while Exchange UM had some nicer UI elements for playing and previewing voicemail messages and some different features, cloud voicemail just comes as a message with a standard Mp3 attachment in the Mailbox. If your using Cloud voicemail with Microsoft teams you do get a play control like


also the Cloud Voicemail service has an Audio transcription service which is reasonably accurate.

Accessing Voice Mails programmatically

Accessing these voice messages is pretty easy they are all just messages in your mailbox (usually the Inbox but they can have been moved to other folders) that have a MessageClass of IPM.Note.Microsoft.Voicemail.UM.CA ref https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxoum/102b3a8b-1aad-4f29-90a3-998262d9fa26 . By default in the Exchange Mailbox there is a WellKnownFolder (in this instance its a Search Folder) called VoiceMail that can then be used in either the Microsoft Graph or EWS to return these voice mail messages. eg for the Microsoft Graph your request should look something like

https://graph.microsoft.com/v1.0/users('gscales@datarumble.com')/mailFolders/voicemail/messages
?$expand=SingleValueExtendedProperties(
$filter=Id%20eq%20'Integer%20%7B00020328-0000-0000-C000-000000000046%7D%20Id%200x6801'%20
or%20Id%20eq%20'String%20%7B00020386-0000-0000-C000-000000000046%7D%20Name%20X-VoiceMessageConfidenceLevel'%20
or%20Id%20eq%20'String%20%7B00020386-0000-0000-C000-000000000046%7D%20Name%20X-VoiceMessageTranscription')&
$top=100&$select=Subject,From,Body,IsRead,Id,ReceivedDateTime 



In my request I'm including the following 3 extended properties that the Teams client also needs to have (actually creating a voice message without these properties will break the voicemail section of the Teams client which is a bug on the Microsoft side and one which 3rd party Migration vendors will need to watch out for). The three properties are firstly

PidTagVoiceMessageDuration - https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxoum/970d4c1c-dcc5-44d2-aab3-16e37805b953 which is the length of the voicemail in seconds. A quick PowerShell trick of getting the length of the voicemail from an Mp3 file is to use the Shell Applicaiton eg

        $shell = New-Object -COMObject Shell.Application
        $folder = Split-Path $Mp3FileName
        $file = Split-Path $Mp3FileName -Leaf
        $shellfolder = $shell.Namespace($folder)
        $shellfile = $shellfolder.ParseName($file) 
        $dt = [DateTime]::ParseExact($shellfolder.GetDetailsOf($shellfile, 27), "HH:mm:ss",[System.Globalization.CultureInfo]::InvariantCulture);
        $dt.TimeOfDay.TotalSeconds


The other two properties are Internet Header properties so could be either returned by requesting all the Internet Headers on the message or using the Extended properties definition

X-VoiceMessageTranscription - currently undocumented but seems to contain the transcription of the voice message (The transcription is also included in the Body of the Message)

and

X-VoiceMessageConfidenceLevel - currently undocumented

I've put together a script which is posted here that will return Voicemail from a Mailbox including the above information using the Microsoft Graph along with the ADAL library for authentication. The script is located https://github.com/gscales/Powershell-Scripts/blob/master/VoiceMailGraph.ps1 eg in action it looks like

Get-VoiceMail -MailboxName e5tmp5@datarumble.com | select @{n="Sender";e={$_.from.emailAddress.address}},Subject,PidTagVoiceMessageDuration,x* |ft

Which will give an output like


To run this type of script will require an App registration with at least Mail.Read or Mail.Read.Shared if you want to access mailboxes other then your own.


Sending Voice Mails programmatically

To Send a VoiceMail message using the Microsoft Graph or EWS is just a matter of sending a Message and setting the ItemClass to IPM.Note.Microsoft.Voicemail.UM.CA, attach the MP3 file and then setting the 3 properties I mentioned above as well as an additional property PidTagVoiceMessageAttachmentOrder. Eg the following is an example of a Microsoft Graph request to do this.



POST https://graph.microsoft.com/v1.0/users('gscales@datarumble.com')/sendmail HTTP/1.1

  "Message" : {
"Subject": "Voice Mail (27 seconds)"
,"Sender":{
 "EmailAddress":{
  "Name":"gscales@datarumble.com",
  "Address":"gscales@datarumble.com"
}}
,"Body": {
"ContentType": "HTML",
"Content": "<html><head>...</html>"
}
,"ToRecipients": [ 
      { 
 "EmailAddress":{
  "Name":"e5tmp5@datarumble.com",
  "Address":"e5tmp5@datarumble.com"
}}
  ]
,  "Attachments": [ 
    {
     "@odata.type": "#Microsoft.OutlookServices.FileAttachment",
     "Name": "audio.mp3",
     "ContentBytes": "..."
    } 
  ]
,"SingleValueExtendedProperties": [
{
"id":"String 0x001A", 
"Value":"IPM.Note.Microsoft.Voicemail.UM.CA"
 } 
,{
"id":"Integer {00020328-0000-0000-c000-000000000046} Id 0x6801", 
"Value":"27"
 } 
,{
"id":"String {00020386-0000-0000-C000-000000000046} Name X-VoiceMessageConfidenceLevel", 
"Value":"high"
 } 
,{
"id":"String {00020386-0000-0000-C000-000000000046} Name X-VoiceMessageTranscription", 
"Value":"one two three"
 },
,{
"id":"String 0x6805", 
"Value":"audio.mp3"
 }  
]
}   ,"SaveToSentItems": "true"
}

Depending on where you got the Mp3 your sending as a voice message you will need to do the transcription yourself eg Azure cognitive services can be used to do this https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-to-text . Or if you want to generate a MP3 from some text you have using PowerShell you can use this cool script that Adam Bertram posted https://mcpmag.com/articles/2019/05/23/text-to-speech-azure-cognitive-services.aspx . I've created a script that you can use to Send a Voicemail using the Microsoft Graph API using the ADAL libraray for Authentication https://github.com/gscales/Powershell-Scripts/blob/master/VoiceMailGraph.ps1  . To use this try something like the following

Send-VoiceMail -MailboxName gscales@datarumble.com -ToAddress e5tmp5@datarumble.com -Mp3FileName C:\temp\SampleAudio_0.4mb.mp3 -Transcription "one two three"

When sending a Voicemail it includes the following HTML table in a Message that contains the sending users personal information eg

To fill out this information in the script I'm making another graph call to look at the users information based on the email address (technically you could use the /me if you never going to send as another user). So the script also makes the following Graph query to get this information

https://graph.microsoft.com/v1.0/users?$filter=mail%20eq%20'gscales@datarumble.com'&$Select=displayName,businessPhones,mobilePhone,mail,jobTitle,companyName

To Send a Message will requires an App Registration with Mail.Send as well as User.Read to be able to read the directory properties required to fill out the Body of the Message with the sending users phone, title and company information.

Doing the same in EWS

You can do the same as above using EWS, instead of the userdetail query the same information can be retrieved using the ResolveName operation in EWS. I've put a sample module for geting and sending voicemail using EWS on Github here informationhttps://github.com/gscales/Powershell-Scripts/blob/master/VoiceMailEWS.ps1

Thursday, May 16, 2019

Outlook Addin for exporting Email to EML from Outlook on the Web

One of the more interesting announcements from the recent Microsoft Build conference was the ability to get the MimeContent of Messages in the Microsoft Graph API https://developer.microsoft.com/en-us/graph/blogs/mime-format-support-for-microsoft-graph-apis-preview/ . This is a much needed feature as it was something that a lot of people use in EWS application, it still comes with a few limitations the Graph has a 4GB underlying limit for REST https://docs.microsoft.com/en-us/graph/api/post-post-attachments?view=graph-rest-1.0&tabs=javascript and its export only at the moment so you can't import messages using it. One other thing is that its only in the Graph endpoint not the Outlook Rest endpoint so its not that easy to use from a Mail Add-in (without additional security config).

One thing I do a bit when developing code for Exchange and Outlook is to look at the MimeContent of Messages as well as the MAPI properties using a MAPI editor like OutlookSpy of MFCMapi. This requires having Outlook installed and a profile configured which can be a bit of pain. These days for my everyday mail uses I always use Outlook on the web or Outlook mobile (for convenience and also they are good clients) so unless I have a specific need for using the Outlook desktop client it usually remains closed. So being able to export a message to Mime from with Outlook on the web is something personally I find useful so I decided to put together a Mail Addin that would allow me to do just this. I would have liked to have used the new graph operation for this, however to use this operation requires some extra integration obstacles that made EWS a better option to use because I just wanted it to work everywhere (across multiple tenants). The only limitation with EWS is that its restricted to a max a 1MB https://docs.microsoft.com/en-us/outlook/add-ins/limits-for-activation-and-javascript-api-for-outlook-add-ins#limits-for-javascript-api so this will only work if the message is less the 1MB which isn't a problem for the majority of things I want to use it for.

How does it work

The addin is pretty simple in that it just makes a EWS GetItem Request for the current Message using the makeEwsRequestAsync method which will include the MimeContent the Message. Then the code will use msSaveOrOpenBlob on Edge or a automated link workaround to decode the base64 contents of the MimeContent returned and present a download back to user in either Chrome or Edge. It action it looks like the following


I've put the code for this addin on GitHub here https://github.com/gscales/gscales.github.io/tree/master/OWAExportAsEML if you want to test this out yourself you can add it straight from my GitHub pages repo using

https://gscales.github.io/OWAExportAsEML/OWAExportAsEML.xml


Wednesday, May 01, 2019

Auditing Inbox rules with EWS and the Graph API in Powershell

There has been a lot of information of late from security researchers and Microsoft themselves about Inbox rules being used to compromise workstations and for use in more pervasive security breaches. One of the more interesting one is is https://blogs.technet.microsoft.com/office365security/defending-against-rules-and-forms-injection/

Which has a pretty nice EWS script https://github.com/OfficeDev/O365-InvestigationTooling/blob/master/Get-AllTenantRulesAndForms.ps1 for enumerating Rules, specifically they are looking for a Client side rule exploit so this script is enumerating all the Extended Rule Objects in the FAI collection of the Inbox. In Exchange you can have Server side rules which run regardless of the connection state of any client or Client only rules which only run when the client is connected for more information see https://support.office.com/en-us/article/server-side-vs-client-only-rules-e1847992-8aa1-4158-8e24-ad043decf1eb

So what the above script does is specifically target looking for a client side rule exploit. However it will return both for Server and Client side extended rule object.

Exchange itself has two different types of rules Standard Rules and Extended rules, the later was a solution to the early Rule size issue that plagued Exchange in early versions. 

Another interesting exploit for rules that released by the following researchers https://blog.compass-security.com/2018/09/hidden-inbox-rules-in-microsoft-exchange/

The exploit talked about in the above is about making a Server side rule hidden so it won't appear when you try to enumerate it with the EXO cmdlet Get-InboxRule (or it also won't appear in Outlook or OWA) or actually any of EWS or Microsoft Graph Rule operations.  To understand why this would occur if you change the value of the  PidTagRuleMessageProvider property on a  Rule object requires a little understanding or the Rule Protocol which is documented in https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxorule/70ac9436-501e-43e2-9163-20d2b546b886 .  


but basically the value of this property is meant to determine who owns and who can edit, delete the rule etc and clients should honour this value and not touch rules that they don't own etc. So Outlook not showing you rules where the value isn't set to "RuleOrganizer" is its way of honouring the protocol and I'm guessing Get-InboxRule (also the EWS GetRule operations) is doing simular. The Rule protocol and storage is used in other functions like the JunkEmail Rule and Out Off Office in Exchange so these Rules also don't show up when using any of these API's or cmdlets which is another example of this protocol in action. Using the script from https://github.com/OfficeDev/O365-InvestigationTooling/blob/master/Get-AllTenantRulesAndForms.ps1 you can also track this exploit by including the PidTagRuleMessageProvider value in the result of the audit, I've created a fork of this script to demonstrate the simple modification necessary https://github.com/gscales/O365-InvestigationTooling/blob/master/Get-AllTenantRulesAndForms.ps1 . If your aware of the Hawk tool this also has some coverage for this https://www.powershellgallery.com/packages/HAWK/1.5.0/Content/User%5CGet-HawkUserHiddenRule.ps1 but this misses the mark at little in that it will find blank or null entries but this can be easily defeated by just setting your own custom value. 

When it comes to enumerating Rules your first port of call would be using the Get-InboxRule cmdlet if your looking to do this vai one of the API you could use Redemption to do it via MAPI, for EWS you would use the InboxRule operation eg 


The Graph API also has the follow operation for returning rules https://docs.microsoft.com/en-us/graph/api/mailfolder-list-messagerules?view=graph-rest-1.0 

Here a simple ADAL script that dumps the Inbox rules of a user


Both of the examples I posted just output the rules back to the pipeline in powershell so you would need to add further logic to test for the particular types of rule that you wanted to audit. For example with the Graph example to show only forwarding rule use

Get-InboxRules -MailboxName gscales@datarumble.com | Where-Object {$_.actions.redirectTo}

From a permissions perspective the EWS example will work either delegate permission assinged to the mailbox using Add-MailboxPermissions or with EWS Impersoantion.

With the Graph API the grant required to run this script is MailboxSettings.Read or MailboxSettings.ReadWrite these grants are only scoped to the current mailbox (no shared mailbox scope) which means for delegate access you can only use this against the current users mailbox. Even if you have delegated rights to another mailbox this operation will fail is you try to run it against that mailbox. There is however an application permission for MailboxSettings using this you could create an appOnly token that could be used to access ever mailbox in your Tenant eg see https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread or you could use my Exch-Rest Module which can do this also https://github.com/gscales/Exch-Rest/blob/master/CertificateAuthentication.md and there is a cmdlet in the module Get-Exr-InboxRules which will return the rules the same as the ADAL example posted above.

One interesting thing is Office365 will now notify you when a new forwarding rule is created in OWA or via the Exo cmdlets eg

The protection console will then give you the details on the forwarding addresses that has been used. This is certainly a good mitigation but it doesn't work if you create Rules via Outlook and you also need be following up on these alerts. Other mitigations like making sure your watching your Exchange audit logs https://docs.microsoft.com/en-us/office365/securitycompliance/enable-mailbox-auditing which is the most effective way of picking up on all the rule update activity. Also keeping an eye on the Message Tracking logs to see changes in the Traffic patterns and large volumes of email going to a certain address and forwarding address reports if you have access under your subscription. As its been since the Melissa virus Messaging security is an area where you need a continuous build of scripts and practices to keep up with emerging threat environment. 

As credential leakage improves with MFA and modern auth the use of Automation like Inbox Rules, Flow and Bot's will become a more favoured attack vector. Eg if an access token becomes compromised and the attacker has access to the mailbox for less the 60 minutes these are the vectors they are going to use to increase their persistance.  





Friday, April 12, 2019

The new Mail.ReadBasic permission for the Microsoft Graph API and how to put it to use

Microsoft have just released the Mail.ReadBasic permission into beta for the Microsoft Graph endpoint https://developer.microsoft.com/en-us/graph/blogs/new-basic-read-access-to-a-users-mailbox/ which is a much needed addition that allows the creation of automation and apps that can just access messages at a more meta information level without having access to the Body or Attachments. Privacy and security are always pressing issues especially around email so this can turn down the privacy concerns while also reduce the security concerns of giving full access to content. 

Lets look at one use case for this new permission which is getting the Message Headers from a Message that has arrived in a users Mailbox that you suspect might be spam but you want the header information to do some analysis. Normally if you wanted to build an app to automate this you would have to at least assign Mail.Read which would give full access to all email content in a Mailbox (either delegated or every mailbox in a tenant in the case of Application permissions). This new grant allows us to just to get the Meta information like TO/From and all the first class properties which now includes the InternetMessageHeaders https://docs.microsoft.com/en-us/graph/api/resources/internetmessageheader?view=graph-rest-1.0

All you need to get going with using this is an application registration with just the Mail.ReadBasic permission assigned (for delegate access you would also need access to their underlying Exchange Folder or Mailbox your going to be querying via the normal Exchange DACL mechanisms).

I've put together a simple script that uses the ADAL for authentication and you can then search for a message based on the Internet MessageId and it will retrieve and then process the antipsam headers so you can look at DKIM,SPF and DMAC information just with this permission grant.


An example of this in use say if we are looking at the last 60 minutes of our trace logs for messages that where FilteredAsSpam (meaning the message ended up in the Junk Mail folder in the Mailbox)


We can take that MessageId and feed it the script cmdlet and get



A few things that are missing for this at the moment are to be really useful it needs to be an Application permission which I believe is coming. The other thing is you really need to be able to enumerate the Folder Name which this restricted at the moment and the ItemClass should be a first class property as you need it to determine the different types of emails you might be detail with.

Thursday, March 28, 2019

How to log EWS Traces to a file in PowerShell

If your using the EWS Managed API in your PowerShell scripts and you need to do some extended debugging to work out why a script isn't working the way you expect in certain environments you can do this by using Tracing as described in https://docs.microsoft.com/en-us/previous-versions/office/developer/exchange-server-2010/dd633676(v=exchg.80) . What this does once it is enabled is it outputs all the requests and responses that are sent to and from the Exchange server so you can see exactly what is taking place and potentially more information on particular errors that are occurring.  So in a EWS Managed API script to enable this you just need to set the TraceEnabled property on the ExchangeService object to true eg

$server.TraceEnabled = $true

And you will then start seeing traces like the following in the console



A much cleaner way of capturing these traces is to configure the EWS Managed API to use a separate log file to log them to a file so you can review them later. To do this it requires that you create a class that implements an Interface of ITraceListener https://github.com/OfficeDev/ews-managed-api/blob/70bde052e5f84b6fee3a678d2db5335dc2d72fc3/Interfaces/ITraceListener.cs .  In C# this a pretty trivial thing to do but in PowerShell its a little more complicated. However using Add-Type in PowerShell gives you the ability to simply define your own custom class that implements the interface and then compile this on the go which then makes it available in your PS Session. The basic steps are

  • You need to define an class that implements the interface (through inheritance) and the methods defined in that interface in this case it only has one called Trace
  • Define your own code to perform the underlying logging in my example its a simple one liner that will append the Tracemessage to a File the path of which is held in the Public Property I've defined in my class 
  • Use Add-Type to compile the class and make it available in your PS Session
  • Create a Instance of the Class you just defined eg here's a function to do it
eg
function TraceHandler(){
$sourceCode = @"
    public class ewsTraceListener : Microsoft.Exchange.WebServices.Data.ITraceListener
    {
        public System.String LogFile {get;set;}
        public void Trace(System.String traceType, System.String traceMessage)
        {
            System.IO.File.AppendAllText(this.LogFile, traceMessage);
        }
    }
"@    

    Add-Type -TypeDefinition $sourceCode -Language CSharp -ReferencedAssemblies $Script:EWSDLL
    $TraceListener = New-Object ewsTraceListener
   return $TraceListener


}

Then in your PS Code just use the Instance (Object) of the Class you just created (first setting the LogFile property to path of the File you want to log to) eg

        $service.TraceEnabled = $true
        $TraceHandlerObj = TraceHandler
        $TraceHandlerObj .LogFile = "c:\Tracing\$MailboxName.log"
        $service.TraceListener = $TraceHandlerObj 

Friday, March 08, 2019

Microsoft Teams Private Chat History Addin for Outlook

Being somebody who is transitioning across from Skype for Business to Teams one of things I missed the most (and found the most frustrating) is the lack of the ability in Outlook and OWA to view the conversation history from Online meetings and private chats in Microsoft Teams. This is especially frustrating when you have an external meeting and your sent an IM that contains some vital information for what you need to do. This information is tracked in your mailbox for compliance reasons in the Teams Chat folder but this folder is hidden so it not accessible to the clients and must be extracted by other means eg. Most people seem to point to doing a compliance search if you need this data https://docs.microsoft.com/en-us/microsoftteams/security-compliance-overview .

Given that the information is in my mailbox and there shouldn't be any privacy concern around accessing it I looked at a few ways of getting access to these TeamsChat messages in OWA and Outlook the first way was using a SearchFolder. This did kind of work but because of a few quirks that the Hidden folder caused was only usable when using Outlook in online mode (which isn't very usable). The next thing I did was look a using an Addin which worked surprising well and was relatively easy to implement. Here is what it looks likes in action all you need to do is find an Email from the user you want to view the Teams chat messages from and then a query will be executed to find the last 100 Chat messages from that user using the Outlook REST endpoint eg



That constructs a query that looks like the following to Outlook REST endpoint


https://outlook.office.com/api/v2.0/me/MailFolders/AllItems/messages?$OrderyBy=ReceivedDateTime Desc&$Top=30&$Select=ReceivedDateTime,bodyPreview,webLink&$filter=SingleValueExtendedProperties/Any(ep: ep/PropertyId eq 'String 0x001a' and ep/Value eq 'IPM.SkypeTeams.Message') and SingleValueExtendedProperties/Any(ep: ep/PropertyId eq 'String 0x5D01' and ep/Value eq 'e5tmp5@datarumble.com')

To break this down a bit first this gets the first 30 messages from the AllItems Search Folder sorted by the ReceivedDateTime

 https://outlook.office.com/api/v2.0/me/MailFolders/AllItems/messages?$OrderyBy=ReceivedDateTime desc&$Top=30
Next this selects the properties we are going to use the table to display, I used body preview because for IM's that generally don't have subjects so getting the body preview text is generally good enough to shown the whole message. But if the message is longer the link is provided which will open up in a new OWA windows using the weblink property which contains a full path to open the Item. One useful things about opening the message this way is you can then click replay and continue a message from IM in email with the body context from the IM (I know this will really erk some Teams people but i think it pretty cool and has proven useful for me).

$Select=ReceivedDateTime,bodyPreview,weblink

Next this is the filter that is applied so it only returns the Teams chat messages (or those messages that have an ItemClass of IPM.SkypeTeams.Message and are from the sender associated with the Message you activate the Addin on. I used the Extended property definition for both of these because firstly there is no equivalent property and for the From address if you used orderby and the a from filter like  and from/emailAddress/address eq 'e5tmp5@datarumble.com' there's a bug that the messages won't sort by the date so you always get the old messages first. Using the extended property fixed that issue but its a little weird.

 $filter=SingleValueExtendedProperties/Any(ep: ep/PropertyId eq 'String 0x001a' and ep/Value eq 'IPM.SkypeTeams.Message') and and SingleValueExtendedProperties/Any(ep: ep/PropertyId eq 'String 0x5D01' and ep/Value eq 'e5tmp5@datarumble.com') One thing I did find after using this for a while is that it didn't work when I got a notification from teams like the following


Because the above notification message came from noreply@email.teams.microsoft.com it couldn't be used in the above query. Looking at the notification message unfortunately there wasn't any other properties that did contain the email address but the full DisplayName of the user was used in the email's displayName so as a quick workaround for these I made use of EWS's resolvename operation to resolve the displayName to an email address and then I could use the Addin even on the notification messages to see the private chat message that was sent to me within OWA without needing to open the Teams app (which if you have teams account in  multiple tenants can be a real pain). So this one turned into a real productivity enhancer for me. (A quick note is that this will only get the Private Chat messages from the user not the Channel Messages).

Want to give it a try yourself ?

I've hosted the files on my GitHub pages so its easy to test (if you like it clone it and host it somewhere else). But all you need to do is add it as a custom addin (if your allowed to) using the
URL-

  https://gscales.github.io/TeamsChatHistory/TeamsChatHistory.xml



The GitHub repository for the Addin can be found here https://github.com/gscales/TeamsChatHistoryOWAAddIn

Tuesday, February 19, 2019

How to unsubscribe from a Mailing list using the Graph API

One of the features that is currently in beta in the Microsoft Graph API is an operation that will let you unsubscribe from any mailing list that supports the List-Unsubscribe header in a message that complies with RFC-2369 (https://docs.microsoft.com/en-us/graph/api/message-unsubscribe?view=graph-rest-beta).

This can have a number of uses one that does come to mind is when you have staff that are leaving the company (or even taking an extended break where they won't be reading their email) and they have signed up to quite of number of mailing lists. As part of your deprovisioning process you can include a script that will unsubscribe from the emails in a Mailbox before you remove the account instead of deleting and hoping the NDR's do it for you.

The RFC state the following for List-Unsubscribe

3.2. List-Unsubscribe

   The List-Unsubscribe field describes the command (preferably using
   mail) to directly unsubscribe the user (removing them from the list).

   Examples:

     List-Unsubscribe: 
     List-Unsubscribe: (Use this command to get off the list)
         
     List-Unsubscribe: 
     List-Unsubscribe: ,
         
Notably the word preferably is used in the RFC which means that from a implementation standpoint you don't have to have an unsubscribe email address to comply with this RFC. One example of this is  LinkedIn which only has URL's for the unsubscribe which requires you click a checkbox etc to unsubscribe which does nullify the usefulness of this somewhat.

To use this operation is pretty simple all you need is the Id of the mail you want to unsubscribe to and then you do a post on the unsubscribe nav

/users('user@domain')/messages/{id}/unsubscribe

I've created a simple ADAL graph script that gets a unique list of un-subscribable  email for the last 1000 emails in a Mailbox and then runs the unsubscribe method on those emails and posted It https://github.com/gscales/Powershell-Scripts/blob/master/Unsubscribe-Emails.ps1.

 I've also added support for  this into my Exch-Rest module which is available from the PowerShell Gallery and GitHub

To show the unsubscribe information for the last 100 messages in the Inbox use

Get-EXRWellKnownFolderItems -MailboxName gscales@datarumble.com -WellKnownFolder Inbox -MessageCount 100 -ReturnUnsubscribeData | select Subject,unsub* | fl

To process all the email from the last 7 days and unsubscribe for that using something like

        $UnSubribeHash = @{}
        -Filter ("receivedDateTime ge " + [DateTime]::Now.AddDays(-7).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")) -ReturnUnsubscribeData | where-object {$_.unsubscribeEnabled -eq $True} | ForEach-Object{
            $Message = $_
            if($Message.unsubscribeEnabled){
                foreach($Entry in $Message.unsubscribeData){
                    if($Entry.contains("mailto:")){
                        if(!$UnSubribeHash.ContainsKey($Entry))
                        {
                            Invoke-EXRUnsubscribeEmail -ItemRESTURI $Message.ItemRESTURI
                        } 
                    }
                }
            }
        }





Friday, January 11, 2019

Create a Microsoft Teams Group Calendar tab application using the Graph API and FullCalendar JavaScript library

Group calendars have always been one of the big asks for in any group collaboration programs back from Lotus Notes to Microsoft Exchange and now Microsoft Teams. There are a few ways of getting a Group calendar working in Teams, one is hosting the OWA web apps see or some other people advocate using a SharePoint calendar and hosting that similarly.

Here is a different method you can use by taking advantage of being able to call the Graph API in a  Tab application.The getSchedule Graph action (still currently in beta) allows you to query up to 62 days of Freebusy information on up to 100 calendars in a single call this makes it a good option for this type of application. So as long as users can view each others calendars or have detailed freebusy permissions the action should return the level of detail required for a Group calendar.   The other thing you can do with the Graph API is get the users photo and build a nice legend for the Group calendar also. To make this visually appealing you need a good calendar display library which there has been a few over the years but a recent one that is really nice is FullCalendar https://fullcalendar.io/ . It ticks all the boxes it looks great, its easy to use and its free to use. To make the calendar appointments from graph appear in the calendar is as easy as building and array from the return JSON from the graph and throwing in a little random color code to break this up and then stitching in the user photos as they are returned asynchronously from the server to build the legend.


 Here are some screenshots of the Tab in action using Graph data




    Daily view


Weekly view


List view


I've put together a separate GitHub repository for all the code that is required for this app https://github.com/gscales/TeamsGroupCalendar and put some detailed install instruction in the Readme in Github.

I'm currently looking for work either contract or fulltime so if you need a creative developer with lots of energy to write C#,JS, NodeJS, Azure or Lambda functions, Messaging DevOps or PowerShell scripts then please contact me  at gscales@msgdevelop.com

Tuesday, January 08, 2019

Converting Folder and ItemIds from the Exchange Management Shell and Audit Log entries using PowerShell and the Graph API in Exchange Online

First a little news about Exchange Identifiers that you may have missed (its not often that something like this changes so its rather exciting)

When you access an item in an Exchange Mailbox store whether its OnPrem or in the Cloud you use the Identifier of the particular item which will vary across whatever API your using. Eg

MAPI - PR_EntryId eg NameSpace.GetItemFromID(EntryId)
EWS -  EWSId eg EmailMessage.Bind(service,ewsid)
Rest -   RestId  eg https://graph.microsoft.com/v1.0/me/messages('restid')
The advice over the years has always been its not a good idea to store these Id's in something like a database because they change whenever and Item is moved. Eg if an Item is moved from the Inbox to a Subfolder in the Inbox it will received a different Id so whatever you have stored in your database suddenly becomes invalid and its not easy to reconcile this. However a new feature that has appeared in Exchange Online in Beta with the Graph API is immutableId's see https://docs.microsoft.com/en-us/graph/outlook-immutable-id the idea behind this is that this Id doesn't change regardless of which folder the item is moved to (or even if its deleted). While it still in Beta at the moment this is a good feature to use going forward if your building synchronization code. Along with immutableId's an operation to Translate Id's between the EntryId, EWS and REST formats is now available in beta in the Graph which is great if your looking to Migrate your MAPI or EWS apps to use the Graph API https://github.com/microsoftgraph/microsoft-graph-docs/blob/master/api-reference/beta/api/user-translateexchangeids.md 

As Audit records are a hot topic of discussion this week with this post from Microsoft another Identifier format you see when using the Exchange Management Shell cmdlets like Get-MailboxFolderStatics is something like



or in an ItemId in a  AuditLog Record like



With these Id's there are just a base64 encoded version of the EntrydId with a leading and trailing byte. So to get back to the Hex version of the Entryid you might be familiar with from a Mapi Editor you can use something like the following



$HexEntryId = [System.BitConverter]::ToString([Convert]::FromBase64String($_.FolderId.ToString())).Replace("-","").Substring(2)  
$HexEntryId =  $HexEntryId.SubString(0,($HexEntryId.Length-2))

This would turn something like

RgAAAAC+HN09lgYnSJDz3kt9375JBwB1EEf9GOowTZ1AsUKLrCDQAAAAAAENAAB1EEf9GOowTZ1AsUKLrCDQAALbJe1qAAAP

Into

00000000BE1CDD3D9606274890F3DE4B7DDFBE490700751047FD18EA304D9D40B1428BAC20D000000000010D0000751047FD18EA304D9D40B1428BAC20D00002DB25ED6A0000

Just having the Id in whatever format isn't much good unless you can do something with it, so I've created a simple Graph script that uses the new user-translateexchangeids.md operation to allow you to translate this Id into an Id that would be useable in other Graph requests. I've create a basic ADAL script version an posted it here on my GitHub https://github.com/gscales/Powershell-Scripts/blob/master/translateEI.ps1

A quick Demo of it in use eg Translate a RestId into an EntryId


Invoke-TranslateExchangeIds -SourceId "AQMkADczNDE4YWE..." -SourceFormat restid -TargetFormat entryid
By default the operation returns a urlsafe base64 encoded results (with padding) so in the script I decode this to the HexEntryId which I find the most useful.

I've also cater for allowing you to post a HexEntryId and the script will automatically encode that for the operations eg


Invoke-TranslateExchangeIds -SourceHexId "00000000BE1CDD3D9606274890F3DE4B7DDFBE49..." -SourceFormat entryid -TargetFormat restid
And it also caters for the encoded EMS format and will strip the extra bytes and covert that eg

Invoke-TranslateExchangeIds -SourceEMSId  $_.FolderId.ToString() -SourceFormat entryid -TargetFormat restid
I've also added this to my Exch-Rest module which is available from the PowerShell Gallery and GitHub which is useful if you want to do some following type things. eg if you wanted to bind to the folder in question you could use


$folderId = Invoke-EXRTranslateExchangeIds -SourceEMSId  $_.FolderId.ToString() -SourceFormat entryid -TargetFormat restid
Get-EXRFolderFromId -FolderId $folderId
Need help with anything I've talked about in this post or need somebody to write C#,JS, NodeJS, Azure or Lambda functions, Messaging DevOps or PowerShell scripts then I'm available now for freelance/contract or fulltime work so please drop me an Email at gscales@msgdevelop.com