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

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()