Sunday, April 1, 2012

Version 1.2 or the EWS Managed API just released

The new version of the EWS Managed API has just been released http://www.microsoft.com/download/en/details.aspx?id=28952 which means you can start using the new Exchange 2010 SP2 features that have been added to the Managed API. A Quck few examples are


First make sure you set the Version to SP2 eg
  1. ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
Using Password Expiration
  1. DateTime PasswordExpirDate = (DateTime)service.GetPasswordExpirationDate("user@domain.com");

Accessing the Directory Photo
  1. NameResolutionCollection ncCol = service.ResolveName("user@domain.com",ResolveNameSearchLocation.DirectoryOnly,true);
  2. foreach (NameResolution nc in ncCol) {
  3. Byte[] dirPhoto = nc.Contact.DirectoryPhoto;
  4. }

See a full list of the new features at http://msdn.microsoft.com/en-us/library/ee693006%28v=exchg.80%29.aspx

Wednesday, March 14, 2012

Find Unused Mailbox Powershell Gui Exchange 2007


Finding unused mailboxes is one of those mind numbing tasks a Sys Admin must perform routinely in Large Exchange organizations. Many of the scripts on this blog help to show ways to tackle different issues around unused or disabled mailboxes such as removing disabled users accounts from groups or finding broken delegate forwarding rules etc. I've also posted a few scripts before that showed some methods to track down unused mailboxes by looking at the number of unread email and the last time a mailbox sent a message. Well this script puts some of these methods together in a powershell GUI script that uses Exchange Web Services and some Exchange Management Shell cmdlets to look at all mailboxes on a server and show us information about when a mailbox was logged into, how big it is, how many unread email there is and when the last sent and/or received email was. It presents all this information back to you as a GUI with data grid and also has a button that allows exporting the result to a CSV file.

How it works?

To get the information about when the mailbox was last logged onto the EMS cmdlet get-mailbox is used and also get-Mailboxstatistics is also used to get the mailbox size in MB. To get the number of unread email in a mailbox and to get the last time a mail was sent from the sent items folder Exchange Web Services is used to query the mailbox. To do this from Powershell and the EMS I’ve used my EWSUtil library which has some routine in there that will perform a finditem operation with the nessasary restrictions so it will only show Unread messages. The library will handle Autodiscover and allow for both delegate and impersonation access method. The Form allows for all of these combinations and also allows you to specify overrides for both authentication and the URL for the CAS server to use (you need to use the full url to the EWS if you going to use eg https://servername/ews/exchange.asmx). The rest of the script just builds the form elements for the GUI and adds some simple function that does a export of the data shown in the GRID.

To use this script you need to have the EwsUtil.dll in c:\temp you can download the library from http://msgdev.mvps.org/exdevblog/ewsutil.zip. You can download the actual script from here I've include a few other scripts in the download that show simpler examples of read the unread email from mailboxes as well (Just for fun). I've put the download of the script here the Code itself looks like


[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$form = new-object System.Windows.Forms.form
[void][Reflection.Assembly]::LoadFile("C:\temp\EWSUtil.dll")
$mbcombCollection = @()
$Emailhash = @{ }

function ExportGrid{

$exFileName = new-object System.Windows.Forms.saveFileDialog
$exFileName.DefaultExt = "csv"
$exFileName.Filter = "csv files (*.csv)|*.csv"
$exFileName.InitialDirectory = "c:\temp"
$exFileName.Showhelp = $true
$exFileName.ShowDialog()
if ($exFileName.FileName -ne ""){
$logfile = new-object IO.StreamWriter($exFileName.FileName,$true)
$logfile.WriteLine("UserName,EmailAddress,Last_Logon,MailboxSize,
Inbox_Number_Unread,Inbox_Unread_LastRecieved,Sent_Items_LastSent")
foreach($row in $msTable.Rows){
$logfile.WriteLine("`"" + $row[0].ToString() + "`"," + $row[1].ToString() + "," + $row[2].ToString() + "," + $row[3].ToString() + "," + $row[4].ToString() + "," + $row[5].ToString() + "," + $row[6].ToString())
}
$logfile.Close()
}

}

function Getinfo(){

$mbcombCollection = @()
$Emailhash.clear()
$msTable.clear()

Get-Mailbox -server $snServerNameDrop.SelectedItem.ToString() -ResultSize Unlimited | foreach-object{
if ($Emailhash.containskey($_.ExchangeGuid) -eq $false){
$Emailhash.add($_.ExchangeGuid.ToString(),$_.windowsEmailAddress.ToString())
}
}

get-mailboxstatistics -server $snServerNameDrop.SelectedItem.ToString() | foreach-object{
$mbcomb = "" | select DisplayName,EmailAddress,Last_Logon,MailboxSize,
Inbox_Number_Unread,Inbox_Unread_LastRecieved,Sent_Items_LastSent
$mbcomb.DisplayName = $_.DisplayName.ToString()
if ($Emailhash.ContainsKey($_.MailboxGUID.ToString())){
$mbcomb.EmailAddress = $Emailhash[$_.MailboxGUID.ToString()]
}
if ($_.LastLogonTime -ne $null){
$mbcomb.Last_Logon = $_.LastLogonTime.ToString()
}
$mbcomb.MailboxSize = $_.TotalItemSize.Value.ToMB()

"Mailbox : " + $mbcomb.EmailAddress
if ($mbcomb.EmailAddress -ne $null){
$mbMailboxEmail = $mbcomb.EmailAddress
if ($unCASUrlTextBox.text -eq ""){ $casurl = $null}
else { $casurl= $unCASUrlTextBox.text}
$useImp = $false
if ($seImpersonationCheck.Checked -eq $true) {
$useImp = $true
}
if ($seAuthCheck.Checked -eq $true) {
$ewc = new-object EWSUtil.EWSConnection($mbMailboxEmail,$useImp, $unUserNameTextBox.Text, $unPasswordTextBox.Text, $unDomainTextBox.Text,$casUrl)
}
else{
$ewc = new-object EWSUtil.EWSConnection($mbMailboxEmail,$useImp, "", "", "",$casUrl)
}


$drDuration = new-object EWSUtil.EWS.Duration
$drDuration.StartTime = [DateTime]::UtcNow.AddDays(-365)
$drDuration.EndTime = [DateTime]::UtcNow

$dTypeFld = new-object EWSUtil.EWS.DistinguishedFolderIdType
$dTypeFld.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::inbox
$dTypeFld2 = new-object EWSUtil.EWS.DistinguishedFolderIdType
$dTypeFld2.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::sentitems

$mbMailbox = new-object EWSUtil.EWS.EmailAddressType
$mbMailbox.EmailAddress = $mbMailboxEmail
$dTypeFld.Mailbox = $mbMailbox
$dTypeFld2.Mailbox = $mbMailbox

$fldarry = new-object EWSUtil.EWS.BaseFolderIdType[] 1
$fldarry[0] = $dTypeFld
$msgList = $ewc.FindUnread($fldarry, $drDuration, $null, "")
$mbcomb.Inbox_Number_Unread = $msgList.Count
if ($msgList.Count -ne 0){
$mbcomb.Inbox_Unread_LastRecieved = $msgList[0].DateTimeSent.ToLocalTime().ToString()
}

$fldarry = new-object EWSUtil.EWS.BaseFolderIdType[] 1
$fldarry[0] = $dTypeFld2
$msgList = $ewc.FindItems($fldarry, $drDuration, $null, "")
if ($msgList.Count -ne 0){
$mbcomb.Sent_Items_LastSent = $msgList[0].DateTimeSent.ToLocalTime().ToString()
}
$msTable.Rows.add($mbcomb.DisplayName,$mbcomb.EmailAddress,$mbcomb.Last_Logon,
$mbcomb.MailboxSize,$mbcomb.Inbox_Number_Unread,
$mbcomb.Inbox_Unread_LastRecieved,$mbcomb.Sent_Items_LastSent)
$mbcombCollection += $mbcomb}
}
$dgDataGrid.DataSource = $msTable
}

$msTable = New-Object System.Data.DataTable
$msTable.TableName = "Mailbox Info"
$msTable.Columns.Add("UserName")
$msTable.Columns.Add("EmailAddress")
$msTable.Columns.Add("Last Logon Time",[DateTime])
$msTable.Columns.Add("Mailbox Size(MB)",[int64])
$msTable.Columns.Add("Inbox Number Unread",[int64])
$msTable.Columns.Add("Inbox unread Last Recieved",[DateTime])
$msTable.Columns.Add("Sent Item Last Sent",[DateTime])



# Add Server DropLable
$snServerNamelableBox = new-object System.Windows.Forms.Label
$snServerNamelableBox.Location = new-object System.Drawing.Size(10,20)
$snServerNamelableBox.size = new-object System.Drawing.Size(80,20)
$snServerNamelableBox.Text = "ServerName"
$form.Controls.Add($snServerNamelableBox)

# Add Server Drop Down
$snServerNameDrop = new-object System.Windows.Forms.ComboBox
$snServerNameDrop.Location = new-object System.Drawing.Size(90,20)
$snServerNameDrop.Size = new-object System.Drawing.Size(150,30)
get-mailboxserver | ForEach-Object{$snServerNameDrop.Items.Add($_.Name)}
$form.Controls.Add($snServerNameDrop)

# Add Export Grid Button

$exButton2 = new-object System.Windows.Forms.Button
$exButton2.Location = new-object System.Drawing.Size(250,20)
$exButton2.Size = new-object System.Drawing.Size(125,20)
$exButton2.Text = "Execute"
$exButton2.Add_Click({Getinfo})
$form.Controls.Add($exButton2)


# Add Export Grid Button

$exButton1 = new-object System.Windows.Forms.Button
$exButton1.Location = new-object System.Drawing.Size(10,610)
$exButton1.Size = new-object System.Drawing.Size(125,20)
$exButton1.Text = "Export Grid"
$exButton1.Add_Click({ExportGrid})
$form.Controls.Add($exButton1)

# Add Impersonation Clause

$esImpersonationlableBox = new-object System.Windows.Forms.Label
$esImpersonationlableBox.Location = new-object System.Drawing.Size(10,75)
$esImpersonationlableBox.Size = new-object System.Drawing.Size(130,20)
$esImpersonationlableBox.Text = "Use EWS Impersonation"
$form.controls.Add($esImpersonationlableBox)

$seImpersonationCheck = new-object System.Windows.Forms.CheckBox
$seImpersonationCheck.Location = new-object System.Drawing.Size(150,70)
$seImpersonationCheck.Size = new-object System.Drawing.Size(30,25)
$form.controls.Add($seImpersonationCheck)

# Add Auth Clause

$esAuthlableBox = new-object System.Windows.Forms.Label
$esAuthlableBox.Location = new-object System.Drawing.Size(10,105)
$esAuthlableBox.Size = new-object System.Drawing.Size(130,20)
$esAuthlableBox.Text = "Specify Credentials"
$form.controls.Add($esAuthlableBox)

$seAuthCheck = new-object System.Windows.Forms.CheckBox
$seAuthCheck.Location = new-object System.Drawing.Size(140,100)
$seAuthCheck.Size = new-object System.Drawing.Size(30,25)
$seAuthCheck.Add_Click({if ($seAuthCheck.Checked -eq $true){
$unUserNameTextBox.Enabled = $true
$unPasswordTextBox.Enabled = $true
$unDomainTextBox.Enabled = $true
}
else{
$unUserNameTextBox.Enabled = $false
$unPasswordTextBox.Enabled = $false
$unDomainTextBox.Enabled = $false}})
$form.controls.Add($seAuthCheck)

# Add UserName Box
$unUserNameTextBox = new-object System.Windows.Forms.TextBox
$unUserNameTextBox.Location = new-object System.Drawing.Size(230,100)
$unUserNameTextBox.size = new-object System.Drawing.Size(100,20)
$form.controls.Add($unUserNameTextBox)

# Add UserName Lable
$unUserNamelableBox = new-object System.Windows.Forms.Label
$unUserNamelableBox.Location = new-object System.Drawing.Size(170,105)
$unUserNamelableBox.size = new-object System.Drawing.Size(60,20)
$unUserNamelableBox.Text = "UserName"
$unUserNameTextBox.Enabled = $false
$form.controls.Add($unUserNamelableBox)

# Add Password Box
$unPasswordTextBox = new-object System.Windows.Forms.TextBox
$unPasswordTextBox.PasswordChar = "*"
$unPasswordTextBox.Location = new-object System.Drawing.Size(400,100)
$unPasswordTextBox.size = new-object System.Drawing.Size(100,20)
$form.controls.Add($unPasswordTextBox)

# Add Password Lable
$unPasswordlableBox = new-object System.Windows.Forms.Label
$unPasswordlableBox.Location = new-object System.Drawing.Size(340,105)
$unPasswordlableBox.size = new-object System.Drawing.Size(60,20)
$unPasswordlableBox.Text = "Password"
$unPasswordTextBox.Enabled = $false
$form.controls.Add($unPasswordlableBox)

# Add Domain Box
$unDomainTextBox = new-object System.Windows.Forms.TextBox
$unDomainTextBox.Location = new-object System.Drawing.Size(550,100)
$unDomainTextBox.size = new-object System.Drawing.Size(100,20)
$form.controls.Add($unDomainTextBox)

# Add Domain Lable
$unDomainlableBox = new-object System.Windows.Forms.Label
$unDomainlableBox.Location = new-object System.Drawing.Size(510,105)
$unDomainlableBox.size = new-object System.Drawing.Size(50,20)
$unDomainlableBox.Text = "Domain"
$unDomainTextBox.Enabled = $false
$form.controls.Add($unDomainlableBox)

# Add CASUrl Box
$unCASUrlTextBox = new-object System.Windows.Forms.TextBox
$unCASUrlTextBox.Location = new-object System.Drawing.Size(280,75)
$unCASUrlTextBox.size = new-object System.Drawing.Size(400,20)
$unCASUrlTextBox.text = $strRootURI
$form.Controls.Add($unCASUrlTextBox)

# Add CASUrl Lable
$unCASUrllableBox = new-object System.Windows.Forms.Label
$unCASUrllableBox.Location = new-object System.Drawing.Size(200,75)
$unCASUrllableBox.size = new-object System.Drawing.Size(50,20)
$unCASUrllableBox.Text = "CASUrl"
$form.Controls.Add($unCASUrllableBox)

# Add DataGrid View

$dgDataGrid = new-object System.windows.forms.DataGridView
$dgDataGrid.Location = new-object System.Drawing.Size(10,145)
$dgDataGrid.size = new-object System.Drawing.Size(1000,450)
$dgDataGrid.AutoSizeRowsMode = "AllHeaders"
$form.Controls.Add($dgDataGrid)

$form.Text = "Exchange 2007 Unused Mailbox Form"
$form.size = new-object System.Drawing.Size(1200,700)
$form.autoscroll = $true
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()

Monday, December 12, 2011

Guest Post – How to Improve Your Exchange Performance

 

With more users each day identifying email as their most important tool, every incremental improvement you can squeeze out of your Exchange performance is victory. Unfortunately, it is the very criticality of email to your users that makes this an uphill battle. As users conduct more and more business using email, their storage needs increase, and as a result, the overall performance of your Exchange system can decrease. Fortunately, there's an easy-to-implement solution to this seeming paradox; and no, it doesn't require your users to delete messages, or for you to restrict them to mailboxes measured in megabytes. Email archiving can provide a big boost to your Exchange performance in several ways. Here are three that can provide you with immediate results.

1. Decreased database size
As databases grow, Exchange must spend more time on database management, more CPU cycles indexing and maintaining the database, and more RAM caching data. Much of what resides in a mailbox database is old; email that has no immediate need, but that users want to keep "just in case". Traditional Exchange management practices limited mailbox sizes which drove users to move old email to PSTs. PSTs are not scalable, and with the disk I/O improvements of Exchange, larger mailboxes reduce the need for PSTs, but these ever larger databases must still be indexed, and defragmented. Implementing an email archiving solution enables you to move older email to another data store on a separate system. Exchange databases are smaller, reducing the overhead associated with maintenance, but users are still able to access their older emails online. It's an easy way to improve Exchange performance.

2. Faster backups and restores
As nice as those huge mailboxes are, there comes a point where the time it takes to backup a database exceeds the time in your backup window. Worse still, should disaster strike, restoring larger databases can take longer than your recovery time objectives. Dividing mailboxes across more and more databases is not a scalable solution, as the administrative overhead of all those databases will soon overwhelm you, and you'd still have to prioritize your restores. Who gets restored first – The CEO or the Payroll team? By implementing email archiving, older messages can be moved to the archive, trimming the size of the mailbox databases without deleting important messages or reducing the available storage. Your Exchange performance gets a huge boost in the backup and restore category.

3. Accessibility
Too many times, the answer to email storage is the use of PST files. PSTs have a number of issues, not the least of which is that email contained within a PST in only accessible to the Outlook client that can physically access the file. Webmail, smartphones, and discovery efforts will all have problems accessing messages stored in PSTs. By implementing an email archiving solution, messages remain accessible to all mail clients, without having to add more and more storage to Exchange. Again, Exchange performance is improved by keeping mail accessible.

As you can see, implementing an email archiving solution is an easy way to boost your Exchange performance while also improving your DR and data accessibility positions.

This guest post was provided by Casper Manes on behalf of GFI Software Ltd. GFI is a leading software developer that provides a single source for network administrators to address their network security, content security and messaging needs. Read more on how to improve your Exchange performance.

All product and company names herein may be trademarks of their respective owners.

Recommended Exchange Reference Material

 

imageWe all have books, whitepapers and documentation that we often end up referring to from time to time, whether it's a refresher on a topic you're a little rusty on or just to double check a few facts. For my benefit, and hopefully yours, here's my handy list of Exchange reference material…

Books

Exchange Server 2010 Inside Out – Tony Redmond
Exchange Server 2010 Best Practices – Siegfried Jagott & Joel Stidley
Exchange 2010 Powershell Cookbook – Mike Pfeiffer

Whitepapers

Best Practices for Virtualizing Exchange Server 2010 with Windows Server 2008 R2 Hyper-V
Exchange 2010 on VMware – Best Practices Guide
Microsoft Exchange Server 2010 Performance on vSphere 5
Best Practices for Exchange Server Public Folders
Publishing Exchange Server 2010 with Forefront UAG 2010 and TMG 2010
Exchange 2010 Large Mailbox Vision Whitepaper

Documentation and Reference

Exchange 2010 SP2 Full Help CHM Dec 2011
Exchange 2010 SP1 Infrastructure Planning and Design Guide
Exchange 2010 EPDS Deployment and Planning Services (EPDS)
JetStress Field Guide
Exchange Server 2010 Architecture Poster
Microsoft Exchange Server 2010 Transport Server Role Architecture Diagrams
RFC 2821 – SMTP
RFC 2822 – Internet Message Format
RFC 3501 – IMAP 4 Rev 1
RFC 1939 – POP3
RFC 2595 – Using TLS with IMAP, POP3 and ACAP
RFC 3261 – Session Initiation Protocol (SIP)

Updated – Recommended Exchange Reference Material

 

Now SP2 has been released, I've updated my list of Exchange Reference Material to contain the link to the new Exchange 2010 SP2 downloadable help file, also available here.

Thanks to Bart Smith for the tip. Bart also recommends Windows PowerShell in Action, Second Edition (which is also recommended as Exchange MCM prep). I haven't personally read it myself, but it looks worth checking out.

Updated – Disabling Auto-mailbox mapping in Exchange 2010

 

imageIn February this year I wrote about how to disable the automatic mapping of shared mailboxes in Exchange 2010 SP1, using a custom PowerShell script that "wraps" the Add-MailboxPermission command and after execution, removes the attribute in Active Directory that is used to automatically mount the mailbox in Outlook.

With Exchange 2010 SP2, there's great news - this workaround is no longer required, as in SP2 a new parameter has been added to the native cmdlet.

Check out the updated article here, and stay tuned for a future article explaining how to extend this functionality to the Exchange Management Console :-)

How to use Multi-Valued Custom Attributes in Exchange 2010 SP2

 

One of the new features in Exchange Server 2010, SP2, is the new ability to use Multi-Valued Custom attributes:

Exchange 2010 SP2 introduces five new multi-value custom attributes that you can use to store additional information for mail recipient objects. The ExtensionCustomAttribute1 to ExtensionCustomAttribute5 parameters can each hold up to 1,300 values. You can specify multiple values as a comma-delimited list.

These attributes (accessible only via the Exchange Management Console) can be used with most types of recipients, including Mailboxes, Mail Contacts, Remote Mailboxes and Distribution Groups, and you could use them to store all kinds of extra text-based information against accounts.

So, how do you use the new features? Well, if you are using Powershell, you can use them by assigning an array to the new Extension Custom Attribute1 - 5 properties. As an example, I'll create a simple array, assign it to ExtensionCustomAttribute1 property and then retrieve it:

image

As it's stored as an array, we can then use PowerShell to add extra attributes to it on demand:

image

In my example, I am using it to record changes. Although it's got limits, it still could be ideal to store a change log of certain actions to an account, such as the date a quota change was applied for example.

By the way, you'll notice the @{Add="Value"} syntax in the example above; if you aren't familiar with the syntax, you can read more here about Using Multivalued Properties in Exchange 2010.