Quantcast
Channel: Mike Pfeiffer's Blog » Exchange Web Services

Sending Email with PowerShell and the EWS Managed API

$
0
0

In this post we'll create a PowerShell advanced function used to send email messages. Instead of using the Send-MailMessage cmdlet or the classes in the System.Net.Mail namespace, we'll use the Exchange Web Services (EWS) Managed API. Using the API to send email is useful because you don't have to relay your messages via smtp – your messages are sent directly through Exchange, and can even be saved in your mailbox sent items folder. We'll also design this function to work with the existing EMS cmdlets so you can pipe in recipient objects and send messages.

The first thing you need to do is download the EWS Managed API. You can download the installer here. Take note of the following requirements before downloading the EWS Managed API:

  • Supported OS: Windows 7, Windows Server 2008, Windows Server 2008 R2, or Windows Vista
  • Requires Microsoft .NET Framework 3.5
  • We'll be using this function from within the Exchange Management Shell, so you'll want to grab the x64 version of the API.

Let's break down the function in a few steps to give you an idea of what is going on.

Creating the Function

We'll start off by creating the function and adding some parameters – we'll call the function Send-EWSMailMessage. We will need to provide parameters for the recipient email address, the subject and the body of the email.

Function Send-EWSMailMessage {
    [CmdletBinding()]
    param(
        [Parameter(Position=0, ValueFromPipelineByPropertyName=$true, Mandatory=$true)]
        [Object]
        $PrimarySmtpAddress,
        [Parameter(Position=1, Mandatory=$true)]
        [System.String]
        $Subject,
        [Parameter(Position=2, Mandatory=$true)]
        [System.String]
        $Body
        )

As you can see, we've defined three parameters; PrimarySmtpAddress, Subject and Body. The PrimarySmtpAddress will provide the address for our email recipient. It will also accept it's value from the pipeline by property name. This means we'll be able to pipe email recipients from the Get-Mailbox and Get-DistributionGroup cmdlets to the Send-EWSEmailMessage function.

The Begin Block

Here we'll import the EWS Managed API assembly and determine the identity of the user running the function. We'll use the email address of the calling user to find an Exchange server using autodiscover and provide the sender address for the message.

begin {
  Add-Type -Path "C:\bin\Microsoft.Exchange.WebServices.dll"
  $sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
  $user = [ADSI]"LDAP://<SID=$sid>"
}

You can see in the code that the path to the assembly is under c:\bin. By default it is located in C:\Program Files\Microsoft\Exchange\Web Services\1.0. Make sure you update your path if needed.

The Process Block

In the process block, we'll handle each object as it comes across the pipeline. We'll create an EWS service object that will locate Exchange using autodiscover using the credentials and identity of the user running the function. Then we'll create a message and send it to the recipient.

process {
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService
    $service.AutodiscoverUrl($user.Properties.mail)

    if($PrimarySmtpAddress.GetType().fullname -eq "Microsoft.Exchange.Data.SmtpAddress") {
        $Recipient = $PrimarySmtpAddress.ToString()
    }
    else {
        $Recipient = $PrimarySmtpAddress
    }
    $mail = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage($service)
    $mail.Subject = $Subject
    $mail.Body = $Body
    [Void] $mail.ToRecipients.Add($Recipient)
    $mail.SendAndSaveCopy()
}

You can see here that we're checking the type of the $PrimarySmtpAddress obejct. This is because depending on how the function was run, it could be a simple string or an object piped from Get-Mailbox or Get-DistributionGroup – if it's piped in as an smtp object, we call the ToString method to get the smtp address. Also notice that we can the SendAndSaveCopy method to send the email, this will leave a copy of the message in the sent items folder in the senders Exchange mailbox.

You can download the finished version of the function here.

Testing it out

Now that we've got the code figured out, we can test the function. First, we'll send a simple message to one user:

$subject = "test"
$body = "this is a test email"
Send-EWSMailMessage administrator@contoso.com $subject $body

Email all users in a specific mailbox database:

Get-Mailbox -Database DB1 | Send-EWSMailMessage -subject $subject -body $body

Send an email to a distribution group:

Get-DistributionGroup Sales | Send-EWSMailMessage -subject $subject -body $body

There's a lot that can be done with the EWS Managed API – I'll be posting more about using it with PowerShell in the future.


Generating Test Email Data for Exchange Labs with PowerShell

$
0
0

I posted a while back about my PowerShell module called ExchangeLab which includes the New-LabMailbox cmdlet used to populate Exchange labs with mailboxes. I've recently updated this module to include a new cmdlet called Send-LabMailMessage that can be used to generate test email data after your test mailboxes have been created.

Here is an overview of how Send-LabMailMessage works:

  • It uses the EWS Managed API, so mail is sent directly through Exchange – this means you don't need to deal with smtp relay and you can let the cmdlet find Exchange using autodiscover.
  • It accepts pipeline input from the Get-Mailbox and Get-DistributionGroup cmdlets – you can quickly populate your labs with email data by simply piping recipients to this cmdlet.
  • It provides Count and MessageSize parameters that allow you to specify the number of messages to be sent to each recipient and how large the message should be (in KB, MB, GB, etc).

Download the module here. Follow the instructions in my previous post on how to install the module to your modules directory. Once the module is installed and imported into your EMS session, you are ready to get started. Below are a couple examples of how you could use the Send-LabMailMessage cmdlet to generate test email data in your lab.

Send an Email to Every Mailbox

This would send a message 1MB in size to every mailbox:

Send Emails to Distribution Groups

This would send a 25 messages to each member of the Sales distribution group. The default message size is 100kb:

This module will work with both Exchange 2007/2010. If you are using 2007, just make sure you have SP2 and PowerShell v2 and the Microsoft .NET Framework 3.5. By default, the Send-LabMailMessage cmdlet is versioned to Exchange 2010. If you are working with Exchange 2007, set the version parameter to Exchange2007_SP1. Also, remember that this cmdlet uses the EWS Managed API, so the user running the cmdlet must have a mailbox in the Exchange organization.

Update 4/27/10 – I modified the Send-LabMailMessage cmdlet so that the messages are not saved in the sent items folder since this could fill up the senders mailbox very quickly. If you want to re-enable this, change line 218 of the ExchangeLab.psm1 file to: $mail.SendAndSaveCopy()

Manage Exchange 2007 Out-Of-Office (OOF) Settings with PowerShell and the EWS Managed API

$
0
0

One of the new features in Exchange 2010 is the ability to manage Out-Of-Office settings for mailboxes from EMS. This is made possible by the Get-MailboxAutoReplyConfiguration and Set-MailboxAutoReplyConfiguration cmdlets. Unfortunately, these cmdlets are only available in Exchange 2010. With Exchange 2007 SP2 or higher, using the Exchange EWS Managed API and PowerShell v2, you can write your own advanced functions that emulate the functionality of the auto reply cmdlets included with 2010. In this post, I'll share a couple of functions that you can use from within EMS that will allow you to do that.

Mailbox Permissions

Before we look at the code, a quick note about permissions. Ideally, we'd like to use Exchange impersonation to access and modify the OOF settings for a user. Unfortunately, EWS impersonation doesn't work with the GetUserOofSettings and SetUserOofSetting methods (see this post for details). So, you'll need to use the Add-MailboxPermission cmdlet to assign your account full access to the mailboxes you want to manage. For more details on Exchange Impersonation vs. Delegate Access, also check out this post.

The Get Function

This code assumes the EWS Managed API assembly is located in c:\bin; update the path as needed for your machine. If you have not yet downloaded the EWS Managed API, you can grab it here. This function is designed to return information similar to the Get-MailboxAutoReplyConfiguration cmdlet in Exchange 2010, here is the code:

function Get-EWSOofSettings {
    [CmdletBinding()]
    param(
        [Parameter(Position=0, ValueFromPipelineByPropertyName=$true, Mandatory=$true)]
        [System.String]
        [Alias("Identity")]
        $PrimarySmtpAddress,
        [Parameter(Position=1, Mandatory=$false)]
        [System.String]
        $ver = "Exchange2007_SP1"    
        )

    begin {
        Add-Type -Path "C:\bin\Microsoft.Exchange.WebServices.dll"
        $sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
        $user = [ADSI]"LDAP://<SID=$sid>"
    }
    
    process {
        $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -Arg $ver
        $service.AutodiscoverUrl($user.Properties.mail)    
        
        if($PrimarySmtpAddress -notmatch "@") {
          $PrimarySmtpAddress = (Get-Recipient $PrimarySmtpAddress).PrimarySMTPAddress.ToString()
        }
        
        $oof = $service.GetUserOofSettings($PrimarySmtpAddress)
        New-Object PSObject -Property @{
            State = $oof.State
            ExternalAudience = $oof.ExternalAudience
            StartTime = $oof.Duration.StartTime
            EndTime = $oof.Duration.EndTime
            InternalReply = $oof.InternalReply
            ExternalReply = $oof.ExternalReply
            AllowExternalOof = $oof.AllowExternalOof
            Identity = (Get-Recipient $PrimarySmtpAddress).Identity
        }
    }
}

Once you have the mailbox permissions worked out and have added the function to your PowerShell session, you can test out the code. The following example shows how you would run the function:

Get-EWSOofSettings -Identity abarlow

The above information is returned for a user with the alias of abarlow. If you've worked with the Get-MailboxAutoReplyConfiguration cmdlet in Exchange 2010, you'll notice that all of the same information is returned.

Finding Users in the Organization with Out-Of-Office Enabled

The Get-EWSOofSettings function is designed to accept pipeline input from the Get-Mailbox cmdet. This means that you could find the settings for multiple users in a single command. For instance, it may be useful to find all users who currently have OOF enabled:

Get-Mailbox | Get-EWSOofSettings | ?{$_.state -eq "Enabled"} | select identity, state

It's worth mentioning here that if a user has a duration set (start and end time) the state will show as "Scheduled". In that case, you would simply modify your where clause to ?{($_.state -eq "Enabled") -or ($_.state -eq "Scheduled")}

The Set Function

Again, this code assumes the EWS Managed API assembly is located in c:\bin, you'll need to update the path accordingly. This function is similar to the Set-MailboxAutoReplyConfiguration cmdlet in Exchange 2010.

function Set-EWSOofSettings {
    [CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$true)]
        [System.String]
        $Identity,
        [Parameter(Position=1, Mandatory=$false)]
        [System.String]
        $State,
        [Parameter(Position=2, Mandatory=$false)]
        [System.String]
        $ExternalAudience,
        [Parameter(Position=3, Mandatory=$false)]
        [System.DateTime]
        $StartTime,
        [Parameter(Position=4, Mandatory=$false)]
        [System.DateTime]
        $EndTime,        
        [Parameter(Position=5, Mandatory=$false)]
        [System.String]
        $InternalReply,
        [Parameter(Position=6, Mandatory=$false)]
        [System.String]
        $ExternalReply,
        [Parameter(Position=7, Mandatory=$false)]
        [System.String]
        $ver = "Exchange2007_SP1"
        )

    begin {
        Add-Type -Path "C:\bin\Microsoft.Exchange.WebServices.dll"
        $sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
        $user = [ADSI]"LDAP://<SID=$sid>"
    }
    
    process {
        $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -arg $ver
        $service.AutodiscoverUrl($user.Properties.mail)    
        
        if($Identity -notmatch "@") {
            $Identity = (Get-Recipient $Identity).PrimarySMTPAddress.ToString()
        }        
        
        $oof = $service.GetUserOofSettings($Identity)
        
        if($StartTime -and $EndTime) {
            $Duration = New-Object Microsoft.Exchange.WebServices.Data.TimeWindow `
            -arg $StartTime,$EndTime
            $PSBoundParameters.Duration = $Duration
            $PSBoundParameters.State = "Scheduled"
            [Void]$PSBoundParameters.remove("StartTime")
            [Void]$PSBoundParameters.remove("EndTime")
        }
        
        foreach($p in $PSBoundParameters.GetEnumerator()) {
            if($p.key -ne "Identity") {
                $oof."$($p.key)" = $p.value                
            }
        }
        $service.SetUserOofSettings($Identity,$oof)
    }
}

Here is an example using the above function to enable out-of-office for a user:

Set-EWSOofSettings -Identity bharris -State Enabled -InternalReply "I am out of the office"

Looking at the function parameters, you can see that several properties can be set including start and end time, internal and external reply, and also external audience.

Here is another example. This will schedule out of office using a start and end time, set the internal and external reply, and configure the external audience to "Known" so that external replies are only sent to recipients in the users contact list:

$start = Get-Date "7/12/2010"
$end = Get-Date "7/15/2010"
Set-EWSOofSettings -Identity jknapp -StartTime $start -EndTime $end -InternalReply "I am out of the office" -ExternalReply "I am out of the office" -ExternalAudience Known

Note that when setting a start and end time, you do not need to provide a value for the state parameter, it will automatically be set to "Scheduled". And finally, to turn off OOF for a user, set the value for the state parameter to "disabled".

Creating Calendar Items with PowerShell and the EWS Managed API

$
0
0

I have a bunch of monitoring scripts that send me e-mail notifications using the Send-MailMessage cmdlet. This has worked well for a while because it reminds me that I need to check on something. The problem I've had lately is that I have tons of e-mail every morning, and often times I'll mark these messages as read, and then forget about them later in the day. So one of the things I've been doing lately is adding these notifications as calendar items in my mailbox. This has worked even better because I can set the start date later in the day and then Outlook and my mobile phone will remind me when I need to check on something.

This is the PowerShell function I wrote that uses the EWS Managed API to create calendar items in an Exchange mailbox:

function New-CalendarItem {
    [CmdletBinding()]
    param(
        [Parameter(Position=1, Mandatory=$true)]
        $Subject,
        [Parameter(Position=2, Mandatory=$true)]
        $Body, 
        [Parameter(Position=3, Mandatory=$true)]
        $Start, 
        [Parameter(Position=4, Mandatory=$true)]
        $End, 
        [Parameter(Position=5, Mandatory=$false)]
        $RequiredAttendees, 
        [Parameter(Position=6, Mandatory=$false)]
        $OptionalAttendees, 
        [Parameter(Position=7, Mandatory=$false)]
        $Location,
        [Parameter(Position=8, Mandatory=$false)]
        $Impersonate
        )

    Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"
    $sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
    $user = [ADSI]"LDAP://<SID=$sid>"        
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList ([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
    $service.AutodiscoverUrl($user.Properties.mail)

    if($Impersonate) {
        $ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress),$Impersonate 
        $service.ImpersonatedUserId = $ImpersonatedUserId   
    }

    $appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $service
    $appointment.Subject = $Subject
    $appointment.Body = $Body
    $appointment.Start = $Start
    $appointment.End = $End 
    
    if($RequiredAttendees) {$RequiredAttendees | %{[void]$appointment.RequiredAttendees.Add($_)}}
    if($OptionalAttendees) {$OptionalAttendees | %{[void]$appointment.RequiredAttendees.Add($_)}}
    if($Location) {$appointment.Location = $Location}
    
    $appointment.Save([Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToAllAndSaveCopy)
}

After you've added the function to your PowerShell session, creating a calendar item is easy. For example, this will create a calendar item that starts in six hours:

$start = (Get-Date).AddHours(6)
$end = $start.AddHours(1)

New-CalendarItem -Subject "Check Disk Free Space" -Body "Make sure that servers are not running out of disk space" -RequiredAttendees helpdesk@contoso.com -Start $start -End $end

In this example, I've used the optional parameter RequiredAttendees to add the helpdesk mailbox as an attendee to the appointment. You can also use this function with the Impersonate parameter to create calendar items in another users mailbox. You'll need to be assigned the application impersonation RBAC role in order for this to work:

New-CalendarItem -Subject "Reboot Server" -Body "Reboot EXCH-SRV01 server after 5PM today" -Start $start -End $end -Impersonate sysadmin@contoso.com

If you want to create calendar items in multiple mailboxes, loop through a collection with the foreach-object cmdlet and run the function for each user. For example, to create an appointment in the mailbox of each member of the ITSupport distribution group:

Get-DistributionGroupMember ITSupport | Foreach-Object{New-CalendarItem -Subject "Install Hotfixes" -Body "Start patching servers after 5PM today" -Start $start -End $end -Impersonate $_.PrimarySMTPAddress}

I've actually found this to be quite useful. I've implemented some basic functionality here, but you can extend this function to do even more. Check out the available members in the Appointment class for more details.

PowerShell Script Cmdlets for Managing E-Mail Items using the Exchange Web Services Managed API

$
0
0

I gave a presentation Wednesday night at the Arizona PowerShell User Group (AZPOSH) on how to get started using the EWS Managed API with PowerShell. I talked about getting connected to EWS using the ExchangeService class, using autodiscover and impersonation, sending e-mails, searching for items in a folder with AQS, moving and deleting items, and viewing message headers. I demonstrated these features using a basic PowerShell module I wrote that includes the following script cmdlets:

  1. Send-EWSMailMessage. Sends an email message from an Exchange mailbox.
  2. Get-EWSMailMessage. Retrieves messages from an Exchange mailbox.
  3. Move-EWSMailMessage. Moves an email message from a source folder to a target folder in the same mailbox.
  4. Remove-EWSMailMessage. Deletes an email message from an Exchange mailbox.
  5. Get-EWSMailMessageHeader. Retrieves the message headers for a single email message.

Each cmdlet includes comment based help and the code is fairly well commented. You can download the module from here. You'll want to unblock the zip file before extracting the folder to one of your $env:PSModulePath directories.

I wanted to post these examples here in case anyone else could get some use out of the code. The cmdlets in this module could use some enhancements though. They run under the security context of the logged on user, the EWS endpoint is set using autodiscover, and the Exchange version will default to Exchange 2010 SP1. If you want to extend this code, it might be useful to add parameters for specifying alternate credentials, manually setting the EWS URL, and specifying the Exchange version.

Testing Exchange Autodiscover with PowerShell and the EWS Managed API

$
0
0

Have you ever used that "Test E-mail AutoConfiguration" utility in Outlook? It's a useful tool that will query the autodiscover service, showing you exactly what CAS URLs are being handed to a particular user, and what the MAPI/HTTP endpoint is for their Exchange mailbox. You can run this tool by pressing the Ctrl button, right clicking on the Outlook 2007/2010 tray icon, and selecting "Test E-mail AutoConfiguration". This is nice, but there are times when I want to be able to determine this information for a user without having to fire up Outlook, or when Outlook isn't installed where ever I happen to be at the moment. My solution for this is a PowerShell function that will retrieve this information using the EWS Managed API. Here's the code:

function Test-Autodiscover {
    [CmdletBinding()]
    param(
      [Parameter(Position=1, Mandatory=$true)]
      [String]
      $EmailAddress,
      
      [ValidateSet("Internal", "External")]
      [Parameter(Position=2, Mandatory=$false)]
      [String]
      $Location = "External",      
      
      [Parameter(Position=3, Mandatory=$false)]
      [System.Management.Automation.PSCredential]
      $Credential,
      
      [Parameter(Position=4, Mandatory=$false)]
      [switch]
      $TraceEnabled,
      
      [Parameter(Position=5, Mandatory=$false)]
      [bool]
      $IgnoreSsl = $true,
      
      [Parameter(Position=6, Mandatory=$false)]
      [String]
      $Url
      )
      
      begin{
        Add-Type -Path 'C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll'
      }
      
      process {
        $autod = New-Object Microsoft.Exchange.WebServices.Autodiscover.AutodiscoverService
        $autod.RedirectionUrlValidationCallback = {$true}
        $autod.TraceEnabled = $TraceEnabled
        
        if($IgnoreSsl) {
          [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}        
        }
        
        if($Credential){
          $autod.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        
        if($Url) {
          $autod.Url = $Url
        }
        
        switch($Location) {
          "Internal" {
            $autod.EnableScpLookup = $true
            $response = $autod.GetUserSettings(
              $EmailAddress,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalEcpUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalEwsUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalOABUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalUMUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalWebClientUrls
            )
            
            New-Object PSObject -Property @{
              RpcClientServer = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer]
              InternalOwaUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalWebClientUrls].urls[0].url
              InternalEcpUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalEcpUrl]
              InternalEwsUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalEwsUrl]
              InternalOABUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalOABUrl]
              InternalUMUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalUMUrl]
            }
          }
          "External" {
            $autod.EnableScpLookup = $false
            $response = $autod.GetUserSettings(
              $EmailAddress,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalMailboxServer,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEcpUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEwsUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalOABUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalUMUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalWebClientUrls
            )
            
            New-Object PSObject -Property @{
              HttpServer = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalMailboxServer]
              ExternalOwaUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalWebClientUrls].urls[0].url
              ExternalEcpUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEcpUrl]
              ExternalEwsUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEwsUrl]
              ExternalOABUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalOABUrl]
              ExternalUMUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalUMUrl]
            }            
          }
        }                
      }
<#
    .SYNOPSIS
        This function uses the EWS Managed API to test the Exchange Autodiscover service.

    .DESCRIPTION
        This function will retreive the Client Access Server URLs for a specified email address
        by querying the autodiscover service of the Exchange server.

    .PARAMETER  EmailAddress
        Specifies the email address for the mailbox that should be tested.

    .PARAMETER  Location
        Set to External by default, but can also be set to Internal. This parameter controls whether
        the internal or external URLs are returned.
        
    .PARAMETER  Credential
        Specifies a user account that has permission to perform this action. Type a user name, such as 
        "User01" or "Domain01\User01", or enter a PSCredential object, such as one from the Get-Credential cmdlet.
        
    .PARAMETER  TraceEnabled
        Use this switch parameter to enable tracing. This is used for debugging the XML response from the server.    
        
    .PARAMETER  IgnoreSsl
        Set to $true by default. If you do not want to ignore SSL warnings or errors, set this parameter to $false.
        
    .PARAMETER  Url
        You can use this parameter to manually specifiy the autodiscover url.        

    .EXAMPLE
        PS C:\> Test-Autodiscover -EmailAddress administrator@uclabs.ms -Location internal
        
        This example shows how to retrieve the internal autodiscover settings for a user.

    .EXAMPLE
        PS C:\> Test-Autodiscover -EmailAddress administrator@uclabs.ms -Credential $cred
        
        This example shows how to retrieve the external autodiscover settings for a user. You can
        provide credentials if you do not want to use the Windows credentials of the user calling
        the function.

    .LINK
        http://msdn.microsoft.com/en-us/library/dd633699%28v=EXCHG.80%29.aspx

#>      
}


There are a few ways to use this Test-Autodiscover function. First, lets say that you are logged on to a domain workstation. Running the function with the following parameters will use your logged on credentials to perform an SCP look up and return the internal settings for the specified e-mail address:

Test-Autodiscover -EmailAddress administrator@uclabs.ms -Location internal

You can omit the -Location parameter, which by default is set to "External", and the function will use DNS to locate autodiscover and retrieve the external settings:

Test-Autodiscover -EmailAddress administrator@uclabs.ms

Finally, you might be testing this from the a machine outside your domain. Just create a PSCredential object using the Get-Credential cmdlet and assign it to the -Credential parameter of the function. For example, here's the result from testing autodiscover for an Exchange Online mailbox hosted through Office 365:

Test-Autodiscover -EmailAddress admin@uclabs.onmicrosoft.com -Credential $cred

In order to use this function you'll need a few things. First, if you haven't already, go download the EWS Managed API. You'll also need the .NET Framework 3.5.1 and PowerShell v2 installed. At that point, all you need to do is add the Test-Autodiscover function to your shell session and you're good to go.

How to use a Proxy Server with the EWS Managed API and PowerShell

$
0
0

You might work in an environment where all web traffic must go through an outbound web proxy. Of course, this can add some complexity when it comes to writing EWS scripts that target an external Exchange organization, such as a partner or a hosted platform like Exchange Online available through Office 365. Luckily, EWS supports the use of a web proxy, and this can be set via the ExchangeService object. Let's take a look at a couple of examples.

First, create an instance of the System.Net.WebProxy class, and set the URI for the proxy:

$proxy = new-object System.Net.WebProxy('http://tmg:8080')

Next, you can set the required user name and password on the $proxy object using the Credentials property, or you can use your logged on Windows credentials by setting the UseDefaultCredentials property to $true:

$proxy.UseDefaultCredentials = $true

Finally, assign your proxy object to the WebProxy property of your ExchangeService object:

$service.WebProxy = $proxy

Here's some code that uses the EWS Managed API 1.1 to send an e-mail message from an Exchange Online mailbox, with all of the web service operations going through an outbound proxy server:

Add-Type -Path 'C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll'
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList Exchange2010_SP1
$service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList  'user@yourdomain.onmicrosoft.com', 'P@ssw0rd'

$proxy = new-object System.Net.WebProxy('http://tmg:8080')
$proxy.UseDefaultCredentials = $true
$service.WebProxy = $proxy

$service.AutodiscoverUrl('user@yourdomain.onmicrosoft.com', {$true})

$message = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service
$message.Subject = 'Test is a test'
$message.Body = 'This message is being sent through EWS with PowerShell'
$message.ToRecipients.Add('sysadmin@contoso.com')
$message.SendAndSaveCopy()

This post was a response to a query from one of my readers — thanks for the question, Nick. As a side note, the above code sample is based on a snippet from my guest post on the Hey Scripting Guy blog from a while back. For more details, you can check it out here.

How To Add a Custom Holiday to an Exchange Calendar with PowerShell and the EWS Managed API

$
0
0

I've been asked several times how to modify my previous function to add custom holidays to a calendar. I figured I would create a new function geared specifically for this task. If you take a look at a typical holiday event on a calendar, like the one below (yes, Golfer's Day really is a holiday, at least for some), you'll notice that its basically just an all day appointment, where the free/busy status is set to free, and the category is set to Holiday. I've added the required changes to the previous function and added the ability to accept pipeline input.

If you're looking for more background on exactly what is going on with a the majority of this code, like getting connected to the end-point on your CAS, specifying credentials, etc., check out my older posts on EWS. Also, Glenn has a great "How-To" series over at his blog. What you need to do is add the following function to your PowerShell session:

function New-Holiday {
    [CmdletBinding(DefaultParametersetName='Identity')]
    param(
        [Parameter(Position=0, ParameterSetName='Identity')]
        $Identity,

        [Parameter(Position=1, Mandatory=$true)]
        $Subject,

        [Parameter(Position=2, Mandatory=$true)]
        $Date,

        [Parameter(Position=3)]
        [Switch]$Impersonate,

        [Parameter(Position=4)]
        $ExchangeVersion = 'Exchange2010_SP2',

        [Parameter(Position=5, ParameterSetName='Pipeline', ValueFromPipelineByPropertyName=$true)]
        $PrimarySmtpAddress,

        [Parameter(Position=6)]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter(Position=7)]
        $EWSUrl
    )

    begin {
        #Load the EWS Assembly
        Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll"
    }

    process {
        #Is the identity coming from the pipeline?
        if($PsCmdlet.ParameterSetName -eq 'Pipeline') {
            $Identity = $PrimarySmtpAddress.ToString()
        }

        #Create the ExchangeService object
        $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList ([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::$ExchangeVersion)

        #If Credential parameter used, set the credentials on the $service object
        if($Credential) {
            $service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $Credential.UserName, $Credential.GetNetworkCredential().Password
        }

        #If EWSUrl parameter not used, locate the end-point using autoD
        if(!$EWSUrl) {
            $service.AutodiscoverUrl($Identity, {$true})
        }
        else {
            $service.Url = New-Object System.Uri -ArgumentList $EWSUrl
        }        

        #If Impersonation parameter used, impersonate the user
        if($Impersonate) {
            $ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress),$Identity
            $service.ImpersonatedUserId = $ImpersonatedUserId
        }

        #Configure the start and end time for this all day event
        $start = (get-date $date)
        $end = $start.addhours(24)

        #Create and save the appointment
        $appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $service
        $appointment.Subject = $Subject
        $appointment.Start = $Start
        $appointment.End = $End
        $appointment.IsAllDayEvent = $true
        $appointment.IsReminderSet = $false
        $appointment.Categories.Add('Holiday')
        $appointment.Location = 'United States'
        $appointment.LegacyFreeBusyStatus = [Microsoft.Exchange.WebServices.Data.LegacyFreeBusyStatus]::Free
        $appointment.Save([Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToNone)
    }
}

Here are a few examples of how to run this code. Keep in mind that to add entries to another persons calendar you'll need to be assigned the Application Impersonaiton RBAC role or have rights to their mailbox.

Here's an example of adding a holiday to my own calendar. I'm already signed into Windows with my own mailbox-enabled Active Directory account, and this could be done in the standard "blue" shell or EMS:

New-Holiday -Identity mike@domain.com -Subject "Golfer's Day" -Date 4/10/13

This function also has an additional parameter set, and it can accept the identity of the mailbox from the pipeline in EMS. This means its easy to create these custom holidays on behalf of other users, in bulk, as long as you have impersonation rights:

Get-Mailbox -OrganizationalUnit Sales | New-Holiday -Subject "Golfer's Day" -Date 4/10/13 -Impersonate

Finally, you can use this function to add custom holidays to the calendars for Office 365 mailboxes. For example, here's how I would add a holiday to my cloud mailbox, using the -Credential parameter to authenticate:

$cred = Get-Credential
New-Holiday -Identity mike@domain.onmicrosoft.com -Subject "Golfer's Day" -Date 4/10/13 -Credential $cred

I've added lots of comments in the code for each phase of the function. Hopefully it will be fairly straight forward if you need to extend it further to meet your specific requirements. One more FYI - prior to posting this, I tested the code on PowerShell v3 with both on prem Exchange 2010 SP2, and Exchange Online (Wave 15) mailboxes with the 1.2 version of the EWS Managed Api and everything worked great.