Wednesday, August 07, 2019

Email Header IpAddress GeoIP report Addin for Outlook and Outlook on the Web in Office365

Something that can be useful from time to time when looking at email delivery issues or email threats is to be able to see the Geographical regions that an email has traversed in its delivery. Usually this information gets stored in the Email Header in the received headers but also depending on the client and services being used the Source IpAddress of the client and other intermediaries may get written in other properties.

Because I needed something last week to do this and couldn't find any other addins to do this I created a pretty simple Outlook addin that

  • Gets the headers from a Message using the REST API in Office365
  • Uses a RegEx to get all the IPAddresses from that header
  • Uses a Set in JavaScript to then de duplicate these IPAddresses
  • Then I used one of the many free GeoIP web services out there to query each of the returned IPAddresses from the Regex matches and finally display the result in a table but to Outlook
For example here is what it returns where run against a normal gray email that was forward to my Office365 Mailbox from Gmail

For a quick diagnostic this information is pretty useful as it tells you where the email traversed and the Org information generally will tell you the cloud providers being used. Doing this on email in my junk email folder that where of a nefarious nature showed that the these emails had transitied through  countries that are the usual suspects in this type of activity.

I've hosted the code up on GitHub here https://github.com/gscales/gscales.github.io/tree/master/MailGeoRoutes so the addin can be added straight from the Repo if you want to try it using (MyAddins - Custom Addins) and

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

This uses REST to get the header so will only work on Office365 but the same thing could also be done using EWS for OnPrem servers from Exchange 2013 onward.


Wednesday, July 24, 2019

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

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-object Microsoft.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 

Thursday, July 11, 2019

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

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") -Method Get -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.


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