Saturday, April 7, 2012

Thoughts on Mailbox Database Distribution in Exchange 2010..

Recently on a mailing list I'm a member of, the following question was asked:
"What is the best practice or guideline when you create/organize databases in your organization? How do you add/organize users into different databases?"
This topic comes up regularly with customers, and it occurred to me that it might be worth putting my thoughts here as well as to the mailing list.
I usually recommend, if most mailbox limits are going to be the same, going for a balanced/random distribution based on the mailbox profile and planned users per DB rather than another factor. And, if you have an entire department on one database, or senior staff – yes you can bring them online “first” in the event of a total loss of all database copies, but the opposite case is also true – you could end up in a situation with just your sales force or senior staff without email. If they are spread out, then at least you don’t lose them all and a part of the organization isn’t totally crippled. Also when it comes to sizing you are unlikely to have departments of identical size so you could end up with complicated sizing for LUNs that are hard to manage.
Of course not everyone wants a random distribution. Thinking of some recent customers, I can give a couple of examples where that isn’t the case..
  • Customer 1 – Has convention already in place, distributing by surname. Same mailbox limits for everyone in the organization, so analysed the surnames of the users who’ll move onto these databases to determine the split of surnames per DB to tie up with the planned users per DB.
  • Customer 2 – Has different mailbox limits for different types of users, so mailbox database and log LUNs are sized to match these limits and user numbers, with balanced distribution across mailbox databases within each “tier”.
However looking at larger environments (100,000+) and going through some of the Exchange Environment Reports people have emailed in, there is a general tendency towards a combination of location (e.g. large, distributed environments) and then spreading the mailboxes across DBs rather than dedicating DBs to department/roles.
I've long been a fan of this way of distributing users - and to assist with keeping things distributed evenly, you can download my database balancing script here.
So, what are your thoughts on mailbox distribution? Let me know in the comments…

Importing Global Address List entries into a user’s Contacts folder

I had a fairly unusual request from one of my customers whilst performing the final stages of an Exchange migration. A while ago, the CEO wasn't able to lookup people to contact from the Global Address List due to connectivity problems and wanted "offline" access to the GAL from his phone.
The customer asked me if it would be possible for me to write a little script to effectively grab the GAL (they aren't a massive company) and copy it into the CEO's mailbox, into a dedicated Contacts folder, for example "OrgContacts". Whilst certainly not a great idea for a larger company, for a few hundred users it's not a bad idea, and in a previous post I'vewritten something very similar, so in my spare time put together the following script…
What it does:
  • Connects to the user mailbox using EWS as a the logged on Administrator, using impersonation.
  • Checks if the dedicated contacts folder exists, and if so empties it.
  • Gets the organization's users who have an email address set and at least a work phone or mobile phone number.
  • Adds a contact for each of the above users, populating the contact's name, company, department, job title, email address and work and mobile phone numbers into the dedicated contacts folder.
Pre-requisites
As with the original script this is based on, you need to set up the impersonal for Exchange Web Services and install the Exchange Web Services Managed API 1.2 before using the script. The script uses the default installation location of the EWS Managed API, so if you've got it installed somewhere else, update the script.
To setup the pre-reqs, follow Setup of Exchange Web Services Impersonation and Installing the Exchange Web Services Managed API from the original article Using Powershell to import contacts into Exchange.
Additionally, the script expects to be used in the following scenario:
  • To be executed from the Exchange Management Shell, so it can get the InternalURL for Exchange Web Services (EWS) from Exchange 2010 SP1 or SP2.
  • The logged-in user is the administrator whom has impersonation rights over the mailbox you wish to copy contacts into.
Using the Copy-OrgContactsToUserContacts.ps1 Script
Once the pre-reqs are satisfied, run the script using the following parameters, substituting <mailbox> with the name of the Mailbox User you wish to create the Contacts folder within:
.\Copy-OrgContactsToUserContacts.ps1 -Mailbox <mailbox> -FolderName OrgContacts
Upon initial and subsequent executions, the script should output similar to shown below:
image
Finally, the new contacts folder, complete with copies of GAL entries should show within the user's mailbox, as shown below in OWA:
image
Download the Copy-OrgContactsToUserContacts.ps1 here as a zip file, and as always if you've got any questions or suggestions for improvement, let me know in the comments below..

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"