Quantcast
Channel: Glen's Exchange and Office 365 Dev Blog
Viewing all 241 articles
Browse latest View live

Reporting on Skype for Business Messaging Activity using the Conversation History Folder in Exchange

$
0
0
Skype for Business (and formally Lync before that) uses the Conversation History folder in an Exchange mailbox to store the history of your IM conversations. One message in your mailbox may represent many messages in an IM conversation where the conversation is broken out itself into an XML string in the Message in the "ConversationXml.{CA2F170A-A22B-4f0a-B899-93439DEC3FBC}" extended property which looks something a little bit like this if you dumped its values

Which comes from the below underlying MAPI property


While Instant Messaging hasn't been around as long as Email the statistics and Reporting on its usage even from third party vendors is pretty underwhelming as the data made available for reporting is limited. If however we take advantage of a Mailbox API like EWS or the Graph API and delve into the above information you can start to produce some more useful statistical reporting about your IM traffic. This could be especially useful at the moment if you want to start measuring more accurately the engagement across Skype vs Teams (in regards to IM anyway). I've created an EWS script for doing this that firstly enumerates all the conversation history items for a particular time frame, then gets the above conversation XML for each conversation thread (this requires a second request to Exchange because of the size of the property). Then it parses the XML into a logentry that can be used either itself or summarized for further reporting. I have 3 sample summary reports that does this in the script eg

Report by date




Report by IM's received From


 Report by IM's Sent To


Or you just have the log stream which you can crunch further in something like excel


I've put the code for this script up on GitHub here https://github.com/gscales/Powershell-Scripts/blob/master/SkypeConversationStats.ps1

The script will work in the cloud or OnPrem, if you are using it onprem make sure you using the -basicAuth switch in the cmdlet as the authentication defaults to using oAuth in Azure. A few examples of running the script


If the sip address your reporting is different from the mailbox SMTP (other then the sip prefix) you need to use the -sipaddress parameter to specify this address. This is used in the Sender calculations just pass this in minus the sip: prefix.  


To Get the last 30 days of conversation logs from a Mailbox in the cloud use

Get-ConversationLog -MailboxName gscales@datarumble.com -Days 30
for OnPrem use

Get-ConversationLog -MailboxName gscales@datarumble.com -Days 30 -basicAuth
To use the Reporting options for report by date (see screenshot 1)


Get-ConversationLog -MailboxName gscales@datarumble.com -Days 30 -reportbyDate 
-htmlReport | Out-file c:\temp\30dayreport.html

if you want the same report in csv use

Get-ConversationLog -MailboxName gscales@datarumble.com -Days 30
 -reportbyDate | ConvertTo-Csv -NoTypeInformation -path c:\temp\30dayreport.csv


Get-ConversationLog -MailboxName gscales@datarumble.com -Days 30
 -reportFrom
To report the top senders that sent this Mailbox an IM

Get-ConversationLog -MailboxName gscales@datarumble.com -Days 30
 -reportFrom
To report the top recipients of IM's


Get-ConversationLog -MailboxName gscales@datarumble.com -Days 30
 -reportTo
Hire me - If you would like to do something similar to this or anything else you see on my blog I'm currently available to help with any Office365,Microsoft Teams, Exchange or Active Directory related development work or scripting, please contact me at gscales@msgdevelop.com(nothing too big or small).




Reporting on Teams private Chat Activity using the TeamChat Folder in Exchange

$
0
0
This is a Segway from my last post on Reporting on Skype for Business Messaging Activity using the Conversation History Folder in Exchange .In this Post I'm going to be looking at the Private chat messages from Microsoft Teams that get stored in a hidden folder called TeamChat (see this post for how to get that folder in EWS) under the conversation History Folder as part of the compliance process for Teams.  As Teams is still a work in process a lot of those compliance properties have changed since my first post about getting this folder and looking at the history of even the limited dataset in my mailboxes there is a lot of fluidity in the compliance information that is being added (so its a little bit of a dogs breakfast in terms of data when you go back a number of months). The compliance properties themselves get added when the substrate process in the cloud copies the Chat Message from the skypespaces backend to the Mailbox.  To give you a visual representation on what these properties look like you can use a MAPI Editor 



Unlike Skype where there was one message that would represent multiple messages in a Chat Thread, with Teams there is a one to one basis for each private Chat message in a Thread. All the interesting meta-information that is needed for reporting is held in the above Extended properties. Take for instance the ToSkypeInternalIds property which contains the list of the recipients for Private Chats stored in a Multivalued string property. (this information also gets stored in the Recipient Collection of the Message in a resolved format)


Instead of SIP addresses like Skype, (for chat anyway) Teams has its own format which contains the AzureAd guid of the user (or Guest). If you want  to resolve this back into an EmailAddress or UPN the Graph API can be used to do this eg if I take a Teams address

8:orgid:fb25746f-fa0c-4f01-b815-a7f7b878786f

using a simple Split to get just the guid portion of this string you can then do a simple Rest Get to get the user information as long as you have an underlying Token to use in the Graph API  eg

$GUID = "8:orgid:fb25746f-fa0c-4f01-b815-a7f7b878786f".Split(':')[2] "https://graph.microsoft.com/v1.0/users('"+$GUID+"')"

I've put together a script that first gets the TeamChat folder using EWS then enumerates the messages in that folder for a particular day time frame and using a number of the extended properties from the first screen shot to build some log entries that can then be summarised into some reports. 

EWS Meet Graph

Because EWS doesn't have a way of resolving the Guids from the From and To Address properties in Teams. I've integrated a bit of Microsoft Graph Code to the do the resolution. Because by default this script uses oAuth we can take the Refresh Token from the first auth used to get into EWS and then get another token for the GraphEndpoint without needing to re-prompt for authentication so this all fits together nicely.


So by default when you run the script you will get flat log entries back like


Or you can Group the threads together (based on the ThreadId) using the -GroupThreads Switch


Like the Conversation History script I have 3 base reports that look something like this in HTML



To use these Reporting options

Get-TeamsIMLog -MailboxName gscales@datarumble.com -Days 30 -reportbyDate 
-htmlReport | Out-file c:\temp\30dayreport.html

if you want the same report in csv use

Get-TeamsIMLog -MailboxName gscales@datarumble.com -Days 30
 -reportbyDate | ConvertTo-Csv -NoTypeInformation -path c:\temp\30dayreport.csv

To report the top senders that sent this Mailbox in Direct Chats

Get-TeamsIMLog -MailboxName gscales@datarumble.com -Days 30
 -reportFrom
To report the top recipients of IM's


Get-TeamsIMLog -MailboxName gscales@datarumble.com -Days 30
 -reportTo
I've put the code for this script up on Git Hub here 

Hire me - If you would like to do something similar to this or anything else you see on my blog I'm currently available to help with any Office365,Microsoft Teams, Exchange or Active Directory related development work or scripting, please contact me at gscales@msgdevelop.com(nothing too big or small).


How to Create a Presence based Distribution List in Office 365

Scripters guide to using Guest Access in Office365 to automate things

$
0
0
Guest access is one of the ways in Office365 of collaborating between different organizations which allows you to give certain people who are outside of your company access to a limited subset of the resources you have in the Cloud. This can be an Office365 unified Group or Microsoft Team but also other workloads like SharePoint and OneDrive can utilize this.
When it comes to scripting there are a number of value add things you can do to automate tasks for different people who have guest accounts in another tenant. The first step to automating with Guest Access is to Authenticate and generate an access token in the Guest tenant.
Getting the Guest Tenants Authorization Endpoint
Before you can authenticate you need to first obtain the Guest tenants Authorization endpoint for the tenant where the Guest Account exists in. To do this you can make a simple Get Request like the following
Invoke-WebRequest -uri ("[https://login.windows.net/{0}/.well-known/openid-configuration" -f "guestdomain.com")
this will return a JSON result that contains the Authorization endpoint for the guest tenant along with other information useful when authenticating.
While Invoke Web Request will do the job fine if you ever what to execute something like this from an Azure Run-book it better to use the httpclient object instead. Here is a simple function to get the Authorization endpoint
function Get-TenantId {
param(
[Parameter(Position = 1, Mandatory = $false)]
[String]$DomainName

)
Begin {
$RequestURL = "https://login.windows.net/{0}/.well-known/openid-configuration" -f $DomainName
Add-Type -AssemblyName System.Net.Http
$handler = New-Object System.Net.Http.HttpClientHandler
$handler.CookieContainer = New-Object System.Net.CookieContainer
$handler.AllowAutoRedirect = $true;
$HttpClient = New-Object System.Net.Http.HttpClient($handler);
$Header = New-Object System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")
$HttpClient.DefaultRequestHeaders.Accept.Add($Header);
$HttpClient.Timeout = New-Object System.TimeSpan(0, 0, 90);
$HttpClient.DefaultRequestHeaders.TransferEncodingChunked = $false
$Header = New-Object System.Net.Http.Headers.ProductInfoHeaderValue("Get-TenantId", "1.1")
$HttpClient.DefaultRequestHeaders.UserAgent.Add($Header);
$ClientResult = $HttpClient.GetAsync([Uri]$RequestURL)
$JsonResponse = ConvertFrom-Json $ClientResult.Result.Content.ReadAsStringAsync().Result
return $JsonResponse.authorization_endpoint
}}
Using the ADAL
Once you have the authorization endpoint your ready to Authenticate, using the ADAL library which is a popular method you would use something like the following (where I’m using the above function to get the endpoint in @line3
Import-Module .\Microsoft.IdentityModel.Clients.ActiveDirectory.dll -Force
$PromptBehavior = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters -ArgumentList Auto
$EndpointUri = Get-Tenantid -DomainName domain.com
$Context = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext($EndpointUri)
$token = ($Context.AcquireTokenAsync("https://graph.microsoft.com","d3590ed6-52b3-4102-aeff-aad2292ab01c","urn:ietf:wg:oauth:2.0:oob",$PromptBehavior)).Result
In the above example I’ve used the preapproved Office appId otherwise if you where to use your own AppId that would need to be authorized in the Guest tenant (which is a always a degree of difficulty when dealing with another companie's IT departments).
Once you have the Token you can then make a Request to the ./me endpoint to find a bit more about your account in the guest tenant eg
$Header = @{
'Content-Type' = 'application\json'
'Authorization' = $token.CreateAuthorizationHeader()
}
Invoke-RestMethod -Headers $Header -Uri "https://graph.microsoft.com/v1.0/me" -Method Get -ContentType "application/json"
Or to get the Groups or Teams your a member of you can use
Invoke-RestMethod -Headers $Header -Uri "https://graph.microsoft.com/v1.0/me/memberOf" -Method Get -ContentType "application/json"
And to get the Members or a particular Team or Group
Invoke-RestMethod -Headers $Header -Uri "https://graph.microsoft.com/v1.0/groups/938383f7-3060-4604-b3a5-cbdb0a5fc90f/members" -Method Get -ContentType "application/json"
Where the Guid (938383f7-3060-4604-b3a5-cbdb0a5fc90f in this instance) is the id you retrieved when you used me/memberOf
This gives you access to all the raw data about each of the members of a Group you might be interacting with. For some real life uses of this take a look at the module section below
Other things you can do with this which I’ll go through below are
  • Export the Group members from  a Guest Teams/Group to CSV
  • Download or Upload Files to shared Team/Group drive
  • Export the Groups Calendar to a CSV file

Using a Module

If your looking for an easier way of using Guest Access check out my Exch-Rest Module on the PowerShell Gallery https://www.powershellgallery.com/packages/Exch-Rest/3.22.0 and gitHub https://github.com/gscales/Exch-Rest . The following are samples for this module
To connect to a tenant as a Guest use
 Connect-EXRMailbox -GuestDomain guestdomain.com -MailboxName gscales@datarumble.com
You can then execute the Me and MemberOf requests using
Get-EXRMe

Get-EXRMemberOf
Export the members of a Group or Team your a member of as a Guest to CSV
The following can be used to Export the members of a Team or Unified Group in a Guest tenant to a CSV file. The Inputs you need a the SMTP address of the Group which you can get from running Get-EXRMemberOf in the Guest Tenant
$Group = Get-EXRUnifedGroups -mail guest@guestdomain.org
$GroupMembers = Get-EXRGroupMembers -GroupId $Group.id -ContactPropsOnly
$GroupMembers | Export-Csv -path c:\temp\groupexport.csv -noTypeInformation
If you wish to include the user photo in the export you can use (although the AppId you use to connect must have access to the userphoto)
$Group = Get-EXRUnifedGroups -mail guest@guestdomain.org
$GroupMembers = Get-EXRGroupMembers -GroupId $Group.id -ContactPropsOnly
$GroupMembers | Export-Csv -path c:\temp\groupexport.csv -noTypeInformation
Downloading a File from Group/Teams Shared Drive (Files) as a Guest
$Group = Get-EXRUnifedGroups -mail guest@guestdomain.org
$FileName = "thenameofthedoc.docs"
$File = Get-EXRGroupFiles -GroupId $Group.id -FileName $FileName
Invoke-WebRequest -Uri $File.'@microsoft.graph.downloadUrl' -OutFile ("C:\downloaddirectory\$FileName)
Export a Groups Calendar to CSV as a Guest
$Group = Get-EXRUnifedGroups -mail guest@guestdomain.org
Get-EXRGroupCalendar -GroupId $Group.id -Export | export-csv -NoTypeInformation -Path c:\temp\calendarexport.csv
By default the next 7 days is exported by the time windows can be tweaked using -starttime and -sndtime parameter in the Get-EXRGroupCalendar cmdlet
Hire me - If you would like to do something similar to this or anything else you see on my blog I'm currently available to help with any Office365,Microsoft Teams, Exchange or Active Directory related development work or scripting, please contact me at gscales@msgdevelop.com (nothing too big or small).

Updates to the Exch-Rest PowerShell Module to support PowerShell Core, Azure Cloud Shell and more ADAL integration options

$
0
0
I've had some time recently to do some much needed updates to my Exch-Rest module so it now supports both Azure Cloud Shell and PowerShell Core on Linux (tested on RHEL,CentOS, Debian and Ubuntu). So now you can logon to an Office365 Mailbox using this Module with Powershell on Linux and send Email or a Skype for Business Message or do some mailbox reporting eg


The requirements on Linux is you need to be using the latest version of PowerShell core installed as per https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-powershell-core-on-linux?view=powershell-6.This ensures that all the required .net Core libraries will be available as older version of .Net core didn't have some of the libraries I'm using and I didn't want to backport for older versions.  Also because there are no Linux forms to interact with for authentication you need to pass in the credentials to use via a PSCredential and the code will use the password grant to get the Token eg
$cred = Get-Credential -UserName gscales@datarumble.com
connect-exrmailbox -MailboxName gscales@datarumble.com -Credential $Cred
Azure Cloud Shell

As Cloud Shell is a browser based version of  PowerShell core running on Linux the same connection method of using the credentials as above is needed.

ADAL Integration

I've also added full integration with the ADAL library for authentication so in addition to the native dependency free script methods the module now distributes the ADAL libraries and supports Authentication using that library as well as Token refreshes for scripts that run over an hour etc (using Acquiretokenasync in the ADAL). This supports the following scenarios such as
To use the ADAL libraries for Logon use the following 

connect-exrmailbox -MailboxName gscales@datarumble.com -useADAL

To use the Never Prompt to use the ADAL Cache
connect-exrmailbox -MailboxName gscales@datarumble.com -useADAL -Prompt Never
For connecting using the currently logged on credentials use
connect-exrmailbox -MailboxName gscales@datarumble.com -useADAL -useLoggedOnCredentials -AADUserName gscales@datarumble.com

(The -AADUserName variable is optional but usually required read the GitHub link in the second bullet point)

For those who want to do something simular and are using EWS you will need something like the following to get the AccessToken using ADAL in a normal PowerShell Script.

$ClientId="d3590ed6-52b3-4102-aeff-aad2292ab01c"
$ResourceURI="https://outlook.office365.com"
$EndpointUri='https://login.microsoftonline.com/common'
$Context=New-ObjectMicrosoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext($EndpointUri)
$AADCredential=New-Object"Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential"-ArgumentList"username@domain.com"
$authResult=[Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions]::AcquireTokenAsync($Context,$ResourceURI,$ClientId,$AADcredential)
if($authResult.Result.AccessToken){
$token=$authResult.Result
}
elseif($authResult.Exception){
throw"An error occured getting access token: $($authResult.Exception.InnerException)"
}
return$token

This also goes along with support for using the Module in an Azure Runbook which was added last month.

The Exch-REST Module is available from the PowerShell Gallery https://www.powershellgallery.com/packages/Exch-Rest and GitHub https://github.com/gscales/Exch-Rest

A big thankyou to all those people who provided feedback on using the module hopefully these upgrades make it easier to use in more scenarios.

Hire me - If you would like to do something similar to this or anything else you see on my blog I'm currently available to help with any Office365,Microsoft Teams, Exchange or Active Directory related development work or scripting, please contact me at gscales@msgdevelop.com (nothing too big or small).

ZAP (Zero-hour auto purge) Junk email reporting for Office365 using EWS and REST

$
0
0
Zero-hour auto purge is one of the features of Office365 that will detect malicious and Spam emails and move them to the Junk email folder for any email that has breached the first level defences and has been delivered to users mailboxes. There is a good description of how it works here but basically when the service learns a particular message was malicious/spam it can retrospectively detect and eliminate/move any simular messages that arrived previously and weren't detected.

This is a good and much need feature as no AntiSpam or Malware solution is perfect (no matter what the vendor say) so there will always be the case where thing slip through. But this very fact is what causes an exposure point where the potentially malicious email sits in the Inbox of end user up until the time its gets zapped. What I wanted to present in this post is a few ways you can measure the amount of the time you may have been vulnerable for and show some methods you can use to look more at messages and it's potential malicious content.

How to detect messages that have been zapped in a EWS and REST script

There is a good reference article for this here , what happens when a Message is Zapped and moved to the Junk Email folder is a Internet Message Headers is added which will also create a underlying Extended property see



We can use  this in a EWS or REST script to do some reporting on. In EWS we can use an Exists Search filter on Messages in the JunkEmail folder to find just messages where this property has been set meaning that these messages have been zapped

 $ZapInfo = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,"X-Microsoft-Antispam-ZAP-Message-Info", [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
$Sfexists = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+Exists($ZapInfo)

In the Graph API we can also do something simular using the following filter

$filter=singleValueExtendedProperties/any(ep:ep/ideq'String {00062008-0000-0000-c000-000000000046} Name X-Microsoft-Antispam-ZAP-Message-Info'andep/valuenenull)

What this does is returns us a collection of Messages that have been zapped, I went a step further in my script and put a DateTime filter around this as well (but technically the Junk Email folder has a default retention period of 30 days so it shouldn't really have a large volume of email). Once you have messages return if you check the datetime the message was received and then using the Time-Span function in PowerShell calculate the TimeSpan  against last modified time (which should have been the time the Message was Zapped and moved to the JunkEmail folder) this will give you a good indication of the time that these messages sat in the Inbox of the user (this becomes your vulnerability period). You can also look at the read setting of the Email to determine if the user had actually read the Message that was Zapped. I've gone a another step further in my reporting script to also do some Antispam analysis of the email headers using some code I previously wrote so you can also look the DKIM,DMARC etc values this mail received as well. So in the end what my reporting script does is checks for Zapped messages in the Junk Email folder for a specific time period and then produces a report on the actually exposure time and relevant information around this so it can be further evaluated. The output of the report is something like this

I've put the EWS script that can do this report on GitHub https://github.com/gscales/Powershell-Scripts/blob/master/ZapStatistics.ps1 To run a Report on a particular mailbox use

 Get-ZapStatistics  -MailboxName mailbox@datarumble.com -startdatetime (Get-Date).AddDays(-14)  | Export-Csv -Path c:\Reports\mailboxName.csv -NoTypeInformation

I've also added the same type of script to my Exch-Rest Module so you can do the same thing using the Microsoft Graph API

 Get-EXRZapStatistics  -MailboxName mailbox@datarumble.com -startdatetime (Get-Date).AddDays(-14)  | Export-Csv -Path c:\Reports\mailboxName.csv -NoTypeInformation

The module is available from the PowerShell Gallery or GitHub

DevOps - Looking at deeper analysis and proactive measure

I thought I'd start including a DevOps section in some of my posts to show how you can delve a bit deeper to look what we are reporting on and some potential proactive measures you might be able to put in place to earn such a DevOps tag. In this section I'm assuming you know enough about development to know your way around objects,methods,properties etc. I'm going to be using my Exch-Rest module because its the best tool I have to do this and its also a good way to improve the module itself (and its free).

Analysis 

So first lets just look at one of the Messages that have been zapped to do this just pull the Messages into a collection like

$Messages =  Get-EXRZapStatistics  -MailboxName mailbox@datarumble.com -startdatetime (Get-Date).AddDays(-14)

Then dump out the first message in the Collection

$Messages[0]

This give us something like



So we have a whole bunch of interesting information about the source that is trying to essentially attack or steal the users credentials. We have

Source SMTP server IP Address in the CIP and SPF Values
We have the Reverse DNS PTR (which tells us the sending server country location)
The HostName of the SMTP server which does resolve in DNS to the same IP Address as the PTR record but the HostName doesn't match
The SCL value of 1 which is pretty low

I can see the message was Read by the user (which isn't good if this is a user). So the next thing that might be useful is looking at the content of the messages to see if it has any attachments. We can do this using the InternetMessageId and the following

$message = Find-EXRMessageFromMessageId -MailboxName gscales@datarumble.com -MessageId "<57a196603977b4b3d9e0680d3119d816 baseny-wroclaw.pl="">"57a196603977b4b3d9e0680d3119d816>

This gives us some information like the following


So this tells us right away there where no Attachments so that is one less thing to worry about it also shows the they tried to fool the target user by putting in a different email address in the SenderName then the actual senders Email address as they where trying to make it appear as if it was a system generated message that came from Microsoft itself.

So now we know that there where no attachments it probably just has a link they want the user to click. To get more information on links in the body of the Message we can use something like the following

$expandedEmail = Get-EXREmailBodyLinks -MailboxName gscales@datarumble.com  -InternetMessageId "<57a196603977b4b3d9e0680d3119d816 baseny-wroclaw.pl="">"57a196603977b4b3d9e0680d3119d816>

and then we can view the links in the email like


Bingo there you have an attempt to fake the domain name to make it look like something that a user may have used before so they might feel confident entering their details into etc. At this point you might want to trawl through what ever logs you have available to see if a user did actually visit that URL and take some urgent action if they did.

Being Proactive

So what I've gone through above are some manual steps above to show that the while you can be assured that the cloud is doing its job to a certain extent there are some extra measures you can take to keep a closer a eye on what potential these malicious email are trying to do and catch any unsuspecting users before something like this becomes a bigger problem for you. The cloud isn't set and forget and if your not doing some extra checks on what is happening in the service your not being as an effective for the company you work for (or on behalf of) as you can be. Some other quick DevOps ideas

  • Setup a Azure Runbook that uses Certificate Authentication and AppOnly tokens that will report on the Zapmessages and do some further automated analysis on a Daily or Weekly basis.
  • Look for correlations based on the information you have in the Zapped messages, if your being targeted by someone there is a good chance that location data, Ipaddress from one attacked maybe used in subsequent ones. So apply those patterns to look at your Message Tracking logs which will give you details about SenderIp ect.
Hire me - If you would like to do something similar to this or anything else you see on my blog I'm currently available to help with any Office365,Microsoft Teams, Exchange or Active Directory related development work or scripting, please contact me at gscales@msgdevelop.com (nothing too big or small).   




Front-line series : What can you do when you receive a malicious email in Office 365

Using the Skype for Business UCWA API in a Microsoft Teams Tab application to show the Skype Conversation history

$
0
0
One of the things you maybe considering in the new year is migrating from Skype for Business to Microsoft Teams. In this post I'm going to demonstrate how you can use the UCWA api (which is the REST API you can use to talk to a Skype for Business server either in Office365 or OnPrem) to access Skype for Business from within the Teams Client via a Teams Tab application. (For those unacquainted with UCWA this the API that is used to Access Skype within OWA).
Why would you want to do this ? its one way of easing migration friction by providing a different level of interoperability (outside of using both clients) and also a way of adding functionality into the Teams client that isn't there currently.  In this post I'm going to look at showing the users Skype conversation history, while this information is also stored in a users Mailbox and also accessiblevia the Graph API, in this app I'm going to use the UCWA API to access the conversation logs via the Skype for Business Online servers and also the conversation transcripts. What you end up with is a Teams  tab that looks like the following



and the Transcripts like (this is POC so mind the formatting)



Using UCWA from a Teams Tab Application

There isn't much difference between using the UCWA API in a Teams Tab application then  from using it in any other application, however UCWA does present some challenges around authentication because of the way the discovery process works.  For a quick recap for those unacquainted please read https://docs.microsoft.com/en-us/skype-sdk/ucwa/developingucwaapplicationsforsfbonline . As part of that process you need to get an AccessToken to make a discovery request to find the SK4B pool server to use and then get another AccessToken for the pool server. So when using the Teams Tab Silent authentication flow you need to execute this twice (which is different and more time consuming then say a normal Graph type application)

Getting the Conversation History in UCWA

Once you have logged into UCWA you need to configured the session to enabled the conversation history as its disabled by default.  This involves doing a Put request against the application resource. with the if/match header set to the ETag. You then need to acknowledge the event this will generate and once that is done your UCWA session will be ready to go, you then just need to query the communication resource to get the links required to access the ConversationLogs from the server. In the sample app I'm only accessing the last 50 items from the server as this is only a POC anyway. When it comes to access the conversation transcripts this requires a Batch request to make it efficient (the max batch size in SK4B in 100) so using a page size of 50 keeps this all working okay. A brief overview of the requests required to access the Conversation history.

  • 1 Get request to get the Conversation Logs which is a list with a link to each of the Conversation Entries
  • Batch Get Request for each of the Conversation Entries which gives back a detail history of each conversation (minus the actual Transcript of the conversation but you do get limited Message preview).
  • If you want the full conversation transcript you use the link from the conversation history to access the Transcript. (in the sample when you click the transcript Cell in the Table it makes this request to the SK4B server to get the Transcript and presents that in a separate Div on the page),
Installing and using this Tab Application 

Like any Teams Tab application it must be hosted somewhere, I'm hosting it out of my GitHub site so the configuration file located in https://github.com/gscales/gscales.github.io/blob/master/TeamsUCWA/app/Config/appconfig.js has the following configuration to ensure it point to the hosted location

const getConfig =()=>{
var config ={
clientId :"eed5c282-249f-46f3-9e18-bde1d0091716",
redirectUri :"/TeamsUCWA/app/silent-end.html",
authwindow :"/TeamsUCWA/app/auth.html",
hostRoot:"https://gscales.github.io",
};
return config;
}

Also the manifest file https://github.com/gscales/gscales.github.io/blob/master/TeamsUCWA/TabPackage/manifest.json  has setting that point to hosted that need to be changed if its hosted elsewhere (just search and replace gscales.github.io)

Application Registration 

To use the UCWA API you need to create an application registration with the following oAuth grants



the applicationId for this registration should then be used to replace the clientid in the appconfig.js . The application registration should use the silent-end.html page as the redirect for authentication. Then the last thing you need to do is make sure that the ApplicationId has been consented to in your Organization eg 


https://login.microsoftonline.com/common/adminconsent?client_id=08401c36-6179-4bbe-9bcc-d34ff51d828f
  
Side Loading - To use custom tab applications you first need to enable side loading of Apps in the Office365 Admin portal ref .The important part is "Sideloading is how you add an app to Teams by uploading a zip file directly to a team. Sideloading lets you test an app as it's being developed. It also lets you build an app for internal use only and share it with your team without submitting it to the Teams app catalog in the Office Store. "

As this is a custom application you need to use the "upload a custom app" link which is available when you click ManageTeam-Apps tab see

(Note if you don't see  the "upload a custom app" check that you have side loading of apps enabled in your tenant config)

What you upload here is a Zip file containing your manifest file and two images that you manifest refers to for eg
{
   "icons": {
    "outline": "Outline32.png",
    "color": "Colour192.png"
  },

For this sample this is located in https://github.com/gscales/gscales.github.io/blob/master/TeamsUCWA/TabPackage/manifest.json

All the code for this post is located in GitHub https://github.com/gscales/gscales.github.io/tree/master/TeamsUCWA

Need help with anything I've talked about in this post or need somebody to write C#,JS, NodeJS, Azure or Lambda functions or PowerShell scripts then I'm available now for freelance/contract or fulltime work so drop me an Email at gscales@msgdevelop.com


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

$
0
0
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

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

$
0
0
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

How to unsubscribe from a Mailing list using the Graph API

$
0
0
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($Entryin$Message.unsubscribeData){
if($Entry.contains("mailto:")){
if(!$UnSubribeHash.ContainsKey($Entry))
{
Invoke-EXRUnsubscribeEmail -ItemRESTURI $Message.ItemRESTURI
}
}
}
}
}





Microsoft Teams Private Chat History Addin for Outlook

$
0
0
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

How to log EWS Traces to a file in PowerShell

$
0
0
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
functionTraceHandler(){
$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-LanguageCSharp-ReferencedAssemblies$Script:EWSDLL
$TraceListener=New-ObjectewsTraceListener
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

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

$
0
0
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.

Auditing Inbox rules with EWS and the Graph API in Powershell

$
0
0
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. 






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

$
0
0
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


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

$
0
0
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

Accessing Microsoft Teams summary records (CDR's) for Calls and Meetings using Exchange Web Services

Script to retrieve all the Office365 (Azure) Tenants an account is associated with

$
0
0
The Azure AD business-to-business (B2B) platform is the underlying tech that drives both guest access in Microsoft Teams,Office365 Groups and also other Azure resources. If your someone who collaborates across a number of different organization or potentially different Community or even school or university groups you might find your MSA or Office365 account starts to accumulate Guest access to different tenants. Depending on what type of access your accruing eg If it just Microsoft Teams access you will see your guest tenancies when logging on to Teams, another way is if you logon to the Azure Portal and hit switch directory you will get a list of Azure Directories your account has an association with. 

However and easier way of doing this is using PowerShell along with the ADAL, Azure Resource Management and Graph API's. I put together the following script that uses first the Azure Resource Management API to make a request that gets all the Tenants associated with your account (same as what you would see if you hit switch directory).


$UserResult=(Invoke-RestMethod-Headers$Header
  -Uri("https://management.azure.com/tenants?api-version=2019-03-01&`
  $includeAllTenantCategories=true")-MethodGet-ContentType"Application/json").value

This returns all the Tenants associated with your user and a lot of information about the domains for the tenants your guesting into eg



The domains information was a little interesting especially seeing all the other domains people had associated in their tenants where I was a Guest. While domains aren't private information finding the bulk of domains a particular organization is associated with isn't that easy (from an outside perspective). For the rest of the script I added some code that would authenticate as a Guest into any tenants my account was associated with and then using the Graph API try to query any Teams or Office365 groups this account is associated with and then try to query for the last 2 conversation items in those Groups/Teams. The script adds information about the Groups to the Tenant object returned for each Tenant the account is associated with and then the conversations to each of the Groups in the object returned. eg so i can do something like this


I've put the script up on GitHub at https://github.com/gscales/Powershell-Scripts/blob/master/Get-TenantsForUser.ps1 this script requires the ADAL dll's for the authentication and it should be located in the same directory as the script is being run from.


How to enable Dark mode in Outlook on Web in Office365 with EWS and PowerShell

$
0
0
Last year at Ignite Microsoft announced Dark mode for Outlook On the Web, while this seem to excite a lot of people I never really caught the buzz. However after taking the plunge after being notification bugged by Outlook this week I've found it to be a nice addition especially if your eyes aren't 100%.

When you enable Dark mode using the slider in Outlook on the Web

 
This changes/creates a setting called "isDarkModeTheme" in the OWA.UserOptions User Configuration Object which is held in the FAI collection (Folder Associated Items) in the Non_IPM_Root of the Mailbox. If you want to enable this setting for a user (or users) programmatically or just want to take stock of who is using this then you can use EWS to Read and Set the value in the OWA.UserOptions User Configuration Object in a Mailbox. (if you want to do this in the Microsoft Graph you will need to cry into your beer at the moment because the Microsoft Graph still doesn't support either user configuration objects or accessing FAI Items 😭😭😭). 

The code to enable dark mode is pretty easy first you need the FolderId for the Non_IPM_Root folder of the Mailbox you want to work with, then bind to the UserConfiguration object which will return the Dictionary from the underlying PR_ROAMING_DICTIONARY property. If Dark mode hasn't been enabled yet then the property shouldn't yet be in the Dictionary but if its is it will either be set to True of False depending on wether its enabled or not. So to Change this all we need is some simple code like the following 

        $folderid=new-objectMicrosoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)
$UsrConfig =[Microsoft.Exchange.WebServices.Data.UserConfiguration]::Bind($service,"OWA.UserOptions", $folderid,[Microsoft.Exchange.WebServices.Data.UserConfigurationProperties]::All)
if($UsrConfig.Dictionary){
if($UsrConfig.Dictionary.ContainsKey("isDarkModeTheme")){
if($Disable.IsPresent){
$UsrConfig.Dictionary["isDarkModeTheme"]=$false
}else{
$UsrConfig.Dictionary["isDarkModeTheme"]=$true
}

}else{
if(!$Disable.IsPresent){
$UsrConfig.Dictionary.Add("isDarkModeTheme",$true)
}
}
}
$UsrConfig.Update()

I've put together a simple script that wraps the above and Oauth authentication and provides two cmdlets for getting and setting Darkmode for Outlook on the Web for a mailbox. Eg 

To Get the Current Dark Mode setting use

 Get-DarkModeSetting -MailboxName mailbox@domain.com

To Enable Dark Mode use

Set-DarkModeSetting -MailboxName mailbox@domain.com (will return Get-DarkModeSetting after the update)

To disable Dark Mode use

Set-DarkModeSetting -MailboxName mailbox@domain.com -Disable

I've put the script up on GitHub https://github.com/gscales/Powershell-Scripts/blob/master/DarkModeMod.ps1 

Viewing all 241 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>