The PowerShell Integrated Scripting Environment (ISE) is a great tool for writing, testing and debugging scripts. Since I don't use the ISE exclusively for Exchange scripting, I don't really need to import the EMS cmdlets everytime I start the ISE. So, I added a custom item to the ISE tools menu that I can use to import the Exchange Management Shell cmdlets whenever it is convenient. To do this, add the following code to your ISE-specific profile (for example: Microsoft.PowerShellISE_profile.ps1). Change the server name to use one of your Exchange 2010 servers:

$psISE.CurrentPowerShellTab.AddOnsMenu.SubMenus.Add(
	"Connect to Exchange",
    {
        $s = New-PSSession -ConfigurationName Microsoft.Exchange `
        -ConnectionUri http://adatum-ex1.adatum.com/PowerShell/ `
        -Authentication Kerberos

        Import-PSSession $s
    },
	"Control+Alt+Z"
)

If you don't want to hard code the server name, you can replace the code in the script block in the with the code from this post. It will import the cmdlets from a random Exchange server in the local AD site.

After you reload your profile or restart the ISE, you'll have a new item under the Add-Ons menu:

Click on Connect to Exchange, or use the keyboard shortcut Ctrl+Alt+Z (you can change this). When the code executes, the Exchange Management Shell cmdlets will be imported using implicit remoting:

At this point, you are ready to start running Exchange Mangement Shell commands in the ISE. Of course, if you're not looking to do anything fancy you can just load the EMS cmdlets in your profile (there is an example in this post) but I thought this was kind of a cool tip. If you haven't worked with the ISE much and want to learn more about it, check out the ISE Help on TechNet.

{ 0 comments }

The next meeting of the AZPOSH User Group will take place tomorrow night (Wednesday, September 1st, 2010) from 6:00PM to 8:00PM at Interface Technical Training in downtown Phoenix. This month we are going to do something completely different and hold a Script Club meeting.

What is a Script Club?
It's basically like a hands on lab, but there is no set topic. You can bring an idea for a script, or come with a problem you've been trying to solve and we'll sort it out. We'll either break out in to small groups or do it as one group depending on how may people we have. We'll have workstations with PowerShell v2 available. I'll have my laptop on hand with a basic lab environment consisting of Active Directory and Exchange 2010. Feel free to bring your own laptop if needed. Click here to read the Script Club rules!

What about free stuff?
Yes, we'll have free pizza and beer sodas!

Do I have to be a PowerShell expert to come to Script Club?
Absolutely not! We encourage PowerShell users of any skill level (from newbie to expert and anywhere in between) to attend!

Where is the Meeting?
Interface Technical Training is centrally located in the Park Central Mall in downtown Phoenix, here is the address:

3110 North Central Avenue, Suite 160
Phoenix, Arizona 85012

If you have any questions, feel free to comment below, or contact me. Hopefully we'll see you there!

{ 0 comments }

Exchange 2010 SP1 includes a few new scripts that you can use to make DAG member maintenance a breeze. It's a simple process; you run a script to put a DAG member into maintenance mode, moving the active databases off the server. After maintenance has been completed, you run another script to take the server out of maintenance mode and then you can redistribute the databases across the DAG. The process is outlined in detail below.

1. Run the StartDagServerMaintenance.ps1 script to put the DAG member in maintenance mode
When you run this script, it moves all the active databases to other DAG members. It pauses the node in the cluster, and sets the DatabaseCopyAutoActivationPolicy mailbox server setting to Blocked. The Suspend-MailboxDatabaseCopy cmdlet is run for each database hosted by the DAG member, and the cluster core resources will be moved to another server in the DAG if needed.

[PS] C:\Program Files\Microsoft\Exchange Server\V14\scripts>.\StartDagServerMaintenance.ps1 -serverName adatum-ex1

2. Run the StopDagServerMaintenance.ps1 script to take the DAG member out of maintenance mode
After maintenance is complete, run this script to take the server out of maintenance mode. It will run the Resume-MailboxDatabaseCopy cmdlet for each database its hosting, resume the node in the cluster, and set the DatabaseCopyAutoActivationPolicy setting to Unrestricted.

[PS] C:\Program Files\Microsoft\Exchange Server\V14\scripts>.\StopDagServerMaintenance.ps1 -serverName adatum-ex1

You can run these scripts remotely from another server with the Exchange tools installed and specify the DAG member using the serverName parameter. Just make sure that the server you are running the scripts from has the Windows Failover Cluster Management tools installed.

3. Run RedistributeActiveDatabases.ps1 script to re-balance the active database copies across the DAG
Finally, you can run this script to redistribute the active mailbox databases across the DAG. There are two options for balancing active database copies within a DAG: by activation preference and by site and activation preference.

[PS] C:\Program Files\Microsoft\Exchange Server\V14\scripts>.\RedistributeActiveDatabases.ps1 -DagName DAG -BalanceDbsByActivationPreference -ShowFinalDatabaseDistribution -Confirm:$false

When using the BalanceDbsByActivationPreference parameter the script tries to move the databases to their most preferred copy based on activation preference, regardless of the AD site. If you use the BalanceDbsBySiteAndActivationPreference parameter, the script will attempt to active the most preferred copy and also try to balance them within each AD site.

There are a number of parameters that can be used with this script. Run get-help .\RedistributeActiveDatabases.ps1 for more details. Also, all of these scripts are documented in Managing Mailbox Database Copies and Managing Database Availability Groups on TechNet.

{ 1 comment }

The Search-AdminAuditLog cmdlet is part of the new administrator audit logging functionality in Exchange 2010 SP1. I used it to write a script that sends an HTML report via e-mail based on the changes made within the organization in the last 24 hours. The details of the command used to make each change include the cmdlet name, the parameters and their assigned values, the user who ran it, and the object that was modified. If you want to read up on administrator audit logging, there are some great posts on it here, and over here. I've also blogged about it a couple times, here and here.

Here is a screenshot of how the report looks:

You can create a scheduled task to run once every day and have the report sent as an HTML formatted message to a specified e-mail address. You just need to supply the recipient, sender and smtp server; here is an example running the script manually:

[PS] C:\>.\AuditLogReport.ps1 -To admin@adatum.com -From audit@adatum.com -SmtpServer adatum-ex1

You can customize the script to limit the amount of information included in the report by modifying the parameters used with the Search-AdminAuditLog cmdlet. For example, you can modify the start and end time to use a shorter time window, or you can limit it to only report on certain cmdlets or user ids.

You can download a copy of the script here.

{ 1 comment }

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".

{ 2 comments }