Saturday, April 7, 2012

ExchangeServerPro.com

A question I am often asked is "Steve, where can I find good training for Exchange 2010". Although official Microsoft instructor-led courses are available or CBT training from companies like Train Signal, one problem many IT pros have is that their organization won't pay to send them on training, which can cost thousands of pounds.
So I am very interested to see that Paul Cunningham, who runs ExchangeServerPro.com, has stepped up and put together an Exchange Server 2010 "Boot Camp" which is - get this - absolutely free!
image
Like Microsoft's own courses, the boot camp is focused on teaching you the core basics you need to set up and administer Exchange 2010 - it isn't aimed at some of the more complex scenarios like multi-site DAG failover design, but then again the equivalent Microsoft courses don't either, and they cost a lot of money to attend. But what this does cover is topics such as:
  • How to build your training lab
  • Setup of Windows and Active Directory
  • Installing Exchange Server 2010
  • Configuring Exchange roles including Mailbox, Client Access and Hub Transport
  • Management of recipients, including Mailboxes, Contacts and Distribution Groups
  • Backup and recovery of Exchange


The topics are split up into four modules, and each module is split up into a number of different videos that are easy to digest - for example the Recipient Management module is split up into 6 videos of around 10 minutes or under.
To sign up for Paul's free training, visit exchangeserverpro.com/training. After a quick sign up process all the training is available on-demand.. Enjoy!

Thursday, April 5, 2012

RunAs PowerShell GUI

This PowerShell GUI will open a specified executable utilizing the RanAs feature.



Here's the code..... Save the below code as FileName.ps1 then open with PowerShell. Edit appropriate variables as needed. Rem out PictureBox1 if you don't have a logo...

########################################################################
#region ByPass PowerShell Signing Policy
get-executionpolicy
set-executionpolicy bypass
########################################################################
#Generated Form Function
function GenerateForm {
########################################################################
# Generated On: 4/5/2012 10:46 PM
# Generated By: Robertm
########################################################################

#region Import the Assemblies
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
#endregion

#region Generated Form Objects
$form1 = New-Object System.Windows.Forms.Form
$button2 = New-Object System.Windows.Forms.Button
$label5 = New-Object System.Windows.Forms.Label
$textBox3 = New-Object System.Windows.Forms.TextBox
$label6 = New-Object System.Windows.Forms.Label
$label4 = New-Object System.Windows.Forms.Label
$label3 = New-Object System.Windows.Forms.Label
$button1 = New-Object System.Windows.Forms.Button
$label2 = New-Object System.Windows.Forms.Label
$label1 = New-Object System.Windows.Forms.Label
$textBox2 = New-Object System.Windows.Forms.TextBox
$textBox1 = New-Object System.Windows.Forms.TextBox
$pictureBox1 = New-Object System.Windows.Forms.PictureBox
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
#endregion Generated Form Objects

#----------------------------------------------
#Generated Event Script Blocks
#----------------------------------------------
#Provide Custom Code for events specified.
$button2_OnClick=
{
#TODO: Open Document

Invoke-Item "c:\Instructions.pdf"

}

$handler_pictureBox1_Click=
{
#TODO: None

}

$handler_button1_Click=
{
#TODO: RunAs PSCredentials

$username = "username"
$password = "password"
$credentials = New-Object System.Management.Automation.PSCredential -ArgumentList @($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))
Start-Process "C:\Program Files (x86)\Utility.exe" -Credential ($credentials)

}

$OnLoadForm_StateCorrection=
{#Correct the initial state of the form to prevent the .Net maximized form issue
$form1.WindowState = $InitialFormWindowState
}

#----------------------------------------------
#region Generated Form Code
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 302
$System_Drawing_Size.Width = 533
$form1.ClientSize = $System_Drawing_Size
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$form1.Name = "form1"
$form1.Text = "Archive Manager Exchange Utility"


$button2.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 173
$System_Drawing_Point.Y = 214
$button2.Location = $System_Drawing_Point
$button2.Name = "button2"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 75
$button2.Size = $System_Drawing_Size
$button2.TabIndex = 12
$button2.Text = "Open Doc"
$button2.UseVisualStyleBackColor = $True
$button2.add_Click($button2_OnClick)

$form1.Controls.Add($button2)

$label5.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 22
$System_Drawing_Point.Y = 219
$label5.Location = $System_Drawing_Point
$label5.Name = "label5"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 156
$label5.Size = $System_Drawing_Size
$label5.TabIndex = 11
$label5.Text = "Open Instructional Document"

$form1.Controls.Add($label5)

$textBox3.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 272
$System_Drawing_Point.Y = 132
$textBox3.Location = $System_Drawing_Point
$textBox3.Name = "textBox3"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 20
$System_Drawing_Size.Width = 235
$textBox3.Size = $System_Drawing_Size
$textBox3.TabIndex = 10
$textBox3.Text = "http://www.maxurmail.com"

$form1.Controls.Add($textBox3)

$label6.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 22
$System_Drawing_Point.Y = 266
$label6.Location = $System_Drawing_Point
$label6.Name = "label6"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 179
$label6.Size = $System_Drawing_Size
$label6.TabIndex = 9
$label6.Text = "Created by Rob McBride"

$form1.Controls.Add($label6)

$label4.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 20
$System_Drawing_Point.Y = 135
$label4.Location = $System_Drawing_Point
$label4.Name = "label4"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 299
$label4.Size = $System_Drawing_Size
$label4.TabIndex = 7
$label4.Text = "When prompted ensure you use the following URL:"

$form1.Controls.Add($label4)

$label3.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 20
$System_Drawing_Point.Y = 103
$label3.Location = $System_Drawing_Point
$label3.Name = "label3"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 388
$label3.Size = $System_Drawing_Size
$label3.TabIndex = 6
$label3.Text = "This will launch the Exchange Utilty under the correct creditntials"

$form1.Controls.Add($label3)


$button1.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 430
$System_Drawing_Point.Y = 260
$button1.Location = $System_Drawing_Point
$button1.Name = "button1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 75
$button1.Size = $System_Drawing_Size
$button1.TabIndex = 5
$button1.Text = "Go"
$button1.UseVisualStyleBackColor = $True
$button1.add_Click($handler_button1_Click)

$form1.Controls.Add($button1)

$label2.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 350
$System_Drawing_Point.Y = 218
$label2.Location = $System_Drawing_Point
$label2.Name = "label2"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 58
$label2.Size = $System_Drawing_Size
$label2.TabIndex = 4
$label2.Text = "Password:"

$form1.Controls.Add($label2)

$label1.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 364
$System_Drawing_Point.Y = 193
$label1.Location = $System_Drawing_Point
$label1.Name = "label1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 44
$label1.Size = $System_Drawing_Size
$label1.TabIndex = 3
$label1.Text = "RunAs:"

$form1.Controls.Add($label1)

$textBox2.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 407
$System_Drawing_Point.Y = 216
$textBox2.Location = $System_Drawing_Point
$textBox2.Name = "textBox2"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 20
$System_Drawing_Size.Width = 100
$textBox2.Size = $System_Drawing_Size
$textBox2.TabIndex = 2
$textBox2.Tag = "password"
$textBox2.Text = "password"

$form1.Controls.Add($textBox2)

$textBox1.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 406
$System_Drawing_Point.Y = 190
$textBox1.Location = $System_Drawing_Point
$textBox1.Name = "textBox1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 20
$System_Drawing_Size.Width = 100
$textBox1.Size = $System_Drawing_Size
$textBox1.TabIndex = 1
$textBox1.Tag = "username"
$textBox1.Text = "username"

$form1.Controls.Add($textBox1)


$pictureBox1.DataBindings.DefaultDataSourceUpdateMode = 0

$pictureBox1.Image = [System.Drawing.Image]::FromFile('C:\Logo.jpg')

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 0
$System_Drawing_Point.Y = -2
$pictureBox1.Location = $System_Drawing_Point
$pictureBox1.Name = "pictureBox1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 89
$System_Drawing_Size.Width = 580
$pictureBox1.Size = $System_Drawing_Size
$pictureBox1.TabIndex = 0
$pictureBox1.TabStop = $False
$pictureBox1.add_Click($handler_pictureBox1_Click)

$form1.Controls.Add($pictureBox1)

#endregion Generated Form Code

#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($OnLoadForm_StateCorrection)
#Show the Form
$form1.ShowDialog()| Out-Null

} #End Function

#Call the Function
GenerateForm

Sunday, April 1, 2012

Sus Activity Logger

I had no idea people were still using this. I wrote this applicaiton many years ago and with the release of WSUS didn't realize it was still viable. Maybe ill update it to work in conjunction with all the new features in WSUS if i get some free time; im pretty busy these days. Anyways, heres the original post (Below)...

Note: I no longer own MidThought.com so the URLs wont work. I'll repost the download links shortly.

Original Post: http://www.wsus.info/index.php?showtopic=417&st=0


I created this application to monitor the activity between my client̢۪s and the SUS server. I needed something simple and easy to use for my corporate work environment. I added a link to the SUS Admin page allowing me to quickly check the status of the client updates and activity.
An example of the application can be viewed at the link below:
http://www.midthough...USLogViewer.asp

Items monitored:

- Client IP
 - Client Activity (Self Update, Download)
 - Client Version (XP, and 2K)
 - Client Function
 - SUS logged Errors
 - Status of Activity (Failed, Succeeded)
 - SUS Message ID’s
 - Item being Updated / Downloaded
 - Time Stamp of Activity

There are four items to this application SUSlogviewer.asp, toc.inc, rightgrey.gif, and kaos_date.asp.

SUSlogviewer.asp is the actual meat of the application. This is the page that is displayed on the SUS administration page. Its location and placement is below:

c:\Inetpub\wwwroot\

Toc.inc is the include file provided by Microsoft displaying the links on the SUS administration page. Its location and placement is below:

c:\Inetpub\wwwroot\autoupdate\administration\shared\inc

Kaos_date.asp is an include file I found on the internet to the SUSlogviewer.asp page allowing the logs date and time to be converted to a understandable format. This page was original code by Chris Burge (chrisb@webkaos.co.uk) I take no credit for it. But it does work well. Its location and placement is below:

c:\Inetpub\wwwroot\

Rightgrey.gif is simply a small gif file that is displayed on the SUSlogviewer.asp page. Its location and placement is below:

c:\Inetpub\wwwroot\

Download Information: http://www.midthough...sus/SUS_App.zip

Enjoy. . . . . .

Getting Size and Item details for single folders in Exchange 2003 via Powershell

While this is now something you can do in one line in Exchange 2007 and 2010 if your migrating or managing 2000/3 you may want to get the Size and Item Count details of one particular mailbox folder for all users on a server eg how big the Inbox,SentItems or Calendar folder is. In Exchange 2003 you could get the Mailbox Size via WMI but to get an individual folder size you need to access the Mailbox itself (eg you can use pfdavadmin etc). To do this via Power shell for all users on the server you first need an ADSI query to get all the mailboxes on a particular server then you can use with WebDAV via the virtual admin root or MAPI via RDO\Redemption http://www.dimastr.com/redemption/ which works well in Powershell. I have two scripts that use show how do to this using each of these API's which I've posted a downloadable copy here

To run these scripts you need to use the Netbios name of the server as a commandline parameter the RDO version of the script looks like


$snServerName = $args[0]

$Global:rptCollection = @()

function QueryMailbox($mb){
$mb
$rdoSession = new-object -com Redemption.RDOSession
$rdoSession.LogonExchangeMailbox($SmtpAddress,$snServerName)
$calendar = $rdoSession.Stores.DefaultStore.GetDefaultFolder(9)




$Itmcnt = "" | select DisplayName,SMTPAddress,ItemCount,Size
$Itmcnt.DisplayName = $displayName
$Itmcnt.SMTPAddress = $SmtpAddress
$Itmcnt.ItemCount = $calendar.Items.Count
$Itmcnt.Size = [System.Math]::Round(($calendar.Fields(235405315) /1024),2)
$Global:rptCollection += $Itmcnt
$Itmcnt
$rdoSession.logoff()
}

function GetUsers(){
$root = [ADSI]'LDAP://RootDSE'
$cfConfigRootpath = "LDAP://" + $root.ConfigurationNamingContext.tostring()
$dfDefaultRootPath = "LDAP://" + $root.DefaultNamingContext.tostring()
$configRoot = [ADSI]$cfConfigRootpath
$dfRoot = [ADSI]$dfDefaultRootPath
$searcher = new-object System.DirectoryServices.DirectorySearcher($configRoot)
$searcher.Filter = '(&(objectCategory=msExchExchangeServer)(cn=' + $snServerName + '))'
$searcher.PropertiesToLoad.Add("cn")
$searcher.PropertiesToLoad.Add("gatewayProxy")
$searcher.PropertiesToLoad.Add("legacyExchangeDN")
$searcher1 = $searcher.FindAll()
foreach ($server in $searcher1){
$snServerEntry = New-Object System.DirectoryServices.directoryentry
$snServerEntry = $server.GetDirectoryEntry()
$snServerName = $snServerEntry.cn
$snExchangeDN = $snServerEntry.legacyExchangeDN
}
$searcher.Filter = '(&(objectCategory=msExchRecipientPolicy)(cn=Default Policy))'
$searcher1 = $searcher.FindAll()
foreach ($recppolicies in $searcher1){
$gwaddrrs = New-Object System.DirectoryServices.directoryentry
$gwaddrrs = $recppolicies.GetDirectoryEntry()
foreach ($address in $gwaddrrs.gatewayProxy){
if($address.Substring(0,5) -ceq "SMTP:"){$dfAddress = $address.Replace("SMTP:@","")}
}

}
$arMbRoot = "https://" + $snServerName + "/exadmin/admin/" + $dfAddress + "/mbx/"
$gfGALQueryFilter = "(&(&(&(& (mailnickname=*)(!msExchHideFromAddressLists=TRUE)(| (&(objectCategory=person)" `
+ "(objectClass=user)(msExchHomeServerName=" + $snExchangeDN + ")) )))))"
$dfsearcher = new-object System.DirectoryServices.DirectorySearcher($dfRoot)
$dfsearcher.Filter = $gfGALQueryFilter
$searcher2 = $dfsearcher.FindAll()
foreach ($uaUsers in $searcher2){
$uaUserAccount = New-Object System.DirectoryServices.directoryentry
$uaUserAccount = $uaUsers.GetDirectoryEntry()
foreach ($address in $uaUserAccount.proxyaddresses){
if($address.Substring(0,5) -ceq "SMTP:"){$uaAddress = $address.Replace("SMTP:","")}
}
$SmtpAddress = $uaAddress
$displayName = $uaUserAccount.DisplayName[0]
QueryMailbox($uaAddress)
}
}

GetUsers
$Global:rptCollection | Export-Csv -NoTypeInformation c:\mailbox.csv

Creating an Out of Office Board using Remote Powershell, Mailtips and the EWS Managed API

Following on from my previous post on using MailTips and one of my other popular posts the FreeBusy board the following script creates an Out of office board that shows in one page peoples out of office status and if the out of office message is turned on it displays the OOF message for that user.Here's a quick picture of what it produces



So how does it work it takes advantage of MailTips within EWS to get the OOF status of a user and what the OOF message is if its set. If you have a large number of users it batches the MailTips request in batches of 100 to ensure the request is successful. To get the list of users to query it takes advantage of using Remote powershell and uses the Get-Mailbox cmdlet.

The following script is setup to work with Office365 but will work OnPremise as well if change around the remote powershell URL.

The following variables are those that need to be configured to run this script

$UserName = "user@domain.com"
$Password = "password"

This is the username and password to use for both the remote powershell connection and it will also be used in the EWS Connection.

$MailboxName = $UserName

This line is setting the Sender email Address for MailTips to the above username if your using UPN's that are different from your SMTP addresses then you will need to change this variable.

$rpRemotePowershell = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -credential $adminCredential -Authentication Basic -AllowRedirection

If your using this in a OnPrem environment then you need to change the URL and auth to suit your environment currently this is setup to connect to Office365.

I've put a download of this script here the script itself looks like


  1. $UserName = "user@domain.com"
  2. $Password = "pasdfawrd."
  3. $mbHash = @{ }
  4. $MailboxName = $UserName
  5. $batchSize = 100
  6. $global:oaBoard = ""
  7. $Mailboxes = @()
  8. $cred = New-Object System.Net.NetworkCredential($UserName,$Password)
  9. $secpassword = ConvertTo-SecureString $Password -AsPlainText -Force
  10. $adminCredential = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $UserName,$secpassword
  11. If(Get-PSSession | where-object {$_.ConfigurationName -eq "Microsoft.Exchange"}){
  12. write-host "Session Exists"
  13. }
  14. else{
  15. $rpRemotePowershell = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -credential $adminCredential -Authentication Basic -AllowRedirection
  16. $importresults = Import-PSSession $rpRemotePowershell
  17. }
  18. get-mailbox -ResultSize unlimited | where-object {$_.HiddenFromAddressListsEnabled -eq $false} | foreach-object{
  19. if ($mbHash.ContainsKey($_.WindowsEmailAddress.ToString()) -eq $false){
  20. $mbHash.Add($_.WindowsEmailAddress.ToString(),$_.DisplayName)
  21. }
  22. $emailAddress = $_.WindowsEmailAddress.ToString()
  23. $Mailboxes += $emailAddress
  24. }
  25. $dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"
  26. [void][Reflection.Assembly]::LoadFile($dllpath)
  27. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
  28. $service.TraceEnabled = $false
  29. $service.Credentials = $cred
  30. $service.autodiscoverurl($MailboxName,{$true})
  31. function GetMailTips{
  32. $mbMailboxFolderURI = New-Object System.Uri($service.url)
  33. $wrWebRequest = [System.Net.WebRequest]::Create($mbMailboxFolderURI)
  34. $wrWebRequest.CookieContainer = New-Object System.Net.CookieContainer
  35. $wrWebRequest.KeepAlive = $false;
  36. $wrWebRequest.Headers.Set("Pragma", "no-cache");
  37. $wrWebRequest.Headers.Set("Translate", "f");
  38. $wrWebRequest.Headers.Set("Depth", "0");
  39. $wrWebRequest.ContentType = "text/xml";
  40. $wrWebRequest.ContentLength = $expRequest.Length;
  41. $wrWebRequest.Timeout = 60000;
  42. $wrWebRequest.Method = "POST";
  43. $wrWebRequest.Credentials = $cred
  44. $bqByteQuery = [System.Text.Encoding]::ASCII.GetBytes($expRequest);
  45. $wrWebRequest.ContentLength = $bqByteQuery.Length;
  46. $rsRequestStream = $wrWebRequest.GetRequestStream();
  47. $rsRequestStream.Write($bqByteQuery, 0, $bqByteQuery.Length);
  48. $rsRequestStream.Close();
  49. $wrWebResponse = $wrWebRequest.GetResponse();
  50. $rsResponseStream = $wrWebResponse.GetResponseStream()
  51. $sr = new-object System.IO.StreamReader($rsResponseStream);
  52. $rdResponseDocument = New-Object System.Xml.XmlDocument
  53. $rdResponseDocument.LoadXml($sr.ReadToEnd());
  54. $RecipientNodes = @($rdResponseDocument.getElementsByTagName("t:RecipientAddress"))
  55. $Datanodes = @($rdResponseDocument.getElementsByTagName("t:OutOfOffice"))
  56. for($ic=0;$ic -lt $RecipientNodes.length;$ic++){
  57. if($Datanodes[$ic].ReplyBody.Message -eq ""){
  58. $global:oaBoard = $global:oaBoard + "<tr>" + "`r`n"
  59. $global:oaBoard = $global:oaBoard + "<td>" + $mbHash[$RecipientNodes[$ic].EmailAddress] + "</td>" + "`r`n"
  60. $global:oaBoard = $global:oaBoard + "<td bgcolor=`"#41A317`">In the Office</td>" + "`r`n"
  61. $global:oaBoard = $global:oaBoard + "<td></td>" + "`r`n"
  62. $global:oaBoard = $global:oaBoard + "</tr>" + "`r`n"
  63. }
  64. else{
  65. $global:oaBoard = $global:oaBoard + "<tr>" + "`r`n"
  66. $global:oaBoard = $global:oaBoard + "<td>" + $mbHash[$RecipientNodes[$ic].EmailAddress] + "</td>" + "`r`n"
  67. $global:oaBoard = $global:oaBoard + "<td bgcolor=`"#153E7E`">Out of the Office</td>" + "`r`n"
  68. $global:oaBoard = $global:oaBoard + "<td>" + $Datanodes[$ic].ReplyBody.Message + "</td>" + "`r`n"
  69. $global:oaBoard = $global:oaBoard + "</tr>" + "`r`n"
  70. }
  71. }
  72. }
  73. $expHeader = @"
  74. <?xml version="1.0" encoding="utf-8"?>
  75. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  76. <soap:Header><RequestServerVersion Version="Exchange2010_SP1" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
  77. </soap:Header>
  78. <soap:Body>
  79. <GetMailTips xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
  80. <SendingAs>
  81. <EmailAddress xmlns="http://schemas.microsoft.com/exchange/services/2006/types">$MailboxName</EmailAddress>
  82. </SendingAs>
  83. <Recipients>
  84. "@
  85. $global:oaBoard = $global:oaBoard + "<table><tr bgcolor=`"#95aedc`">" +"`r`n"
  86. $global:oaBoard = $global:oaBoard + "<td align=`"center`" style=`"width=200;`" ><b>User</b></td>" +"`r`n"
  87. $global:oaBoard = $global:oaBoard + "<td align=`"center`" style=`"width=200;`" ><b>Status</b></td>" +"`r`n"
  88. $global:oaBoard = $global:oaBoard + "<td align=`"center`" style=`"width=200;`" ><b>Message</b></td>" +"`r`n"
  89. $global:oaBoard = $global:oaBoard + "</tr>" + "`r`n"
  90. $expRequest = $expHeader
  91. $bCount =0
  92. foreach($mbMailbox in $Mailboxes){
  93. $bCount++
  94. $expRequest = $expRequest + "<Mailbox xmlns=`"http://schemas.microsoft.com/exchange/services/2006/types`"><EmailAddress>$mbMailbox</EmailAddress></Mailbox>"
  95. if($bCount -eq $batchSize){
  96. $expRequest = $expRequest + "</Recipients><MailTipsRequested>OutOfOfficeMessage</MailTipsRequested></GetMailTips></soap:Body></soap:Envelope>"
  97. GetMailTips
  98. $expRequest = $expHeader
  99. $bCount = 0
  100. }
  101. }
  102. if($bCount -ne 0){
  103. $expRequest = $expRequest + "</Recipients><MailTipsRequested>OutOfOfficeMessage</MailTipsRequested></GetMailTips></soap:Body></soap:Envelope>"
  104. GetMailTips
  105. }
  106. $global:oaBoard = $global:oaBoard + "</table>" + " "
  107. $global:oaBoard | out-file "c:\oofboard.htm"

401 errors in Exchange Online using EWS WSDL proxy code and raw HTML

If your developing EWS code against Exchange Online or possibly another large load balanced Hosted Exchange system you may find that a lot of the sample code you see on MSDN or other places that uses EWS Proxy code will always return 401 errors when you try to use it against Exchange Online (The Managed API isn't affected) eg a standard example is something like http://msdn.microsoft.com/en-us/library/bb408524%28v=EXCHG.140%29.aspx. What can cause this problem is the failure to process the exchangecookie in an appropriate way. There is description of what the exchangecookie is used for on http://blogs.msdn.com/b/exchangedev/archive/2011/07/20/client-access-server-affinity-and-network-load-balancing-considerations-for-programmatic-access-to-exchange-online.aspx . Although this says nothing about Authentication if the request object your using be its a SOAP proxy or HttpwebRequest doesn't have the cookiecontainer initialized (which by default they don't) before you submit the first request you'll get a 401. So basically if you have a normal EWS sample after you set the URL or Credentials make sure you initialize the cookie container.eg.

esb.Url = "https://CAS01.contoso.com/EWS/exchange.asmx";
esb..CookieContainer = new CookieContainer();


If your using Raw HTML and a HttpWebRequest make sure you do the same thing before submitting the first request.

A code sample is worth a 1000 words so here's a basic Exchange Online/Office365 Autodiscover and WDSL proxy sample that just does a simple GetFolder to the Inbox.

String MbMailbox = "user@domain.onmicrosoft.com";
String AutoDiscoURL = "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml";
NetworkCredential ncCred = new NetworkCredential("user@domain.onmicrosoft.com","password");
String EWSURL = null;
String auDisXML = "" +
"" + MbMailbox + "" +
"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a" +
"
" +
"
";
System.Net.HttpWebRequest adAutoDiscoRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(AutoDiscoURL);


byte[] bytes = Encoding.UTF8.GetBytes(auDisXML);
adAutoDiscoRequest.ContentLength = bytes.Length;
adAutoDiscoRequest.ContentType = "text/xml";
adAutoDiscoRequest.Headers.Add("Translate", "F");
adAutoDiscoRequest.Method = "POST";
adAutoDiscoRequest.Credentials = ncCred;

Stream rsRequestStream = adAutoDiscoRequest.GetRequestStream();
rsRequestStream.Write(bytes, 0, bytes.Length);
rsRequestStream.Close();
adAutoDiscoRequest.AllowAutoRedirect = false;
WebResponse adResponse = adAutoDiscoRequest.GetResponse();
String Redirect = adResponse.Headers.Get("Location");
if (Redirect != null)
{
adAutoDiscoRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(Redirect);
adAutoDiscoRequest.ContentLength = bytes.Length;
adAutoDiscoRequest.ContentType = "text/xml";
adAutoDiscoRequest.Headers.Add("Translate", "F");
adAutoDiscoRequest.Method = "POST";
adAutoDiscoRequest.Credentials = ncCred;
rsRequestStream = adAutoDiscoRequest.GetRequestStream();
}
rsRequestStream.Write(bytes, 0, bytes.Length);
rsRequestStream.Close();
adResponse = adAutoDiscoRequest.GetResponse();
Stream rsResponseStream = adResponse.GetResponseStream();
XmlDocument reResponseDoc = new XmlDocument();
reResponseDoc.Load(rsResponseStream);
XmlNodeList pfProtocolNodes = reResponseDoc.GetElementsByTagName("Protocol");
foreach (XmlNode xnNode in pfProtocolNodes)
{
XmlNodeList adChildNodes = xnNode.ChildNodes;
foreach (XmlNode xnSubChild in adChildNodes)
{
switch (xnSubChild.Name)
{
case "EwsUrl": EWSURL = xnSubChild.InnerText;
break;
}
}
}
ExchangeServiceBinding esExchangeServiceBinding = new ExchangeServiceBinding();
esExchangeServiceBinding.Credentials = ncCred;
esExchangeServiceBinding.Url = EWSURL;
esExchangeServiceBinding.CookieContainer = new CookieContainer();
DistinguishedFolderIdType[] dsFolderid = new DistinguishedFolderIdType[1];
dsFolderid[0] = new DistinguishedFolderIdType();
dsFolderid[0].Id = DistinguishedFolderIdNameType.inbox;
GetFolderType gfFolderType = new GetFolderType();
gfFolderType.FolderIds = dsFolderid;
gfFolderType.FolderShape = new FolderResponseShapeType();
gfFolderType.FolderShape.BaseShape = DefaultShapeNamesType.Default;
GetFolderResponseType gfGetFolderResponse = esExchangeServiceBinding.GetFolder(gfFolderType);
if (gfGetFolderResponse.ResponseMessages.Items[0].ResponseClass == ResponseClassType.Success) {
FolderType Folder = (FolderType)((FolderInfoResponseMessageType)gfGetFolderResponse.ResponseMessages.Items[0]).Folders[0];
Console.WriteLine(Folder.DisplayName);
}

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