Blog Post

Exchange Team Blog
4 MIN READ

Scripting Corner Volume 2 - Pipelining and the One Line command

The_Exchange_Team's avatar
The_Exchange_Team
Platinum Contributor
Sep 28, 2007

EDIT: This post has been updated on 10/15/2007 to incorporate feedback we received since original posting. So my last post demonstrated a script that used a .net method and some functions within the script to take care of a problem that was a little hard to solve manually. The feedback I received was generally positive but some folks indicated "why is PowerShell useful to me"... "why should I lean how to script these are all one off situations"? To an extent I will agree with you. If you are a smaller shop say 1 to 2 servers then there might not be as much value in leaning to write complex Powershell scripts. Most of what you are going to do on the server is going to be for one off situations (create storage groups, create databases, move mailboxes, etc). So for this installment of my scripting corner I have tried to come up with command that meet two criteria. 1) The command can be written on "one line" within PowerShell and 2) The command is something that I could generally see being used on more than one occasion by the same company. The scenario that I came up with is one we see here on occasion; where an end user will send out an email to an unintentionally large group of users or will send a message out with information that went to a distribution group or set of users that it should not have. In both of these cases there is not a good end user powered way to get these messages back out of the Database once they have been delivered. For Exchange 2000 and 2003 we had the Exmerge tool that could extract these out for us. In Exchange 2007 we have the Export-Mailbox cmdlet. So here we have the command that you can run that will go thru the organization and move all of these unexpected messages to a target mailbox. Get-mailboxserver | foreach {Get-mailbox –server $_.identity -resultsize unlimited | export-mailbox –subjectkeywords "Bad Message" -targetmailbox <newmailbox> -targetfolder Deleteme –deletecontent –confirm:$false} So let's break this one liner down for the key pieces that it uses. The biggest thing that we use here is pipelining "|". The pipeline allows us to take the output of one cmdlet and send it directly to another cmdlet. The cmdlet further down the pipeline will need to understand the output of the proceeding cmdlet, but as long as you use a little common sense this is usually not an issue. We are also using foreach (the alias of foreach-object) to setup an execution loop. When pipelining in from another cmdlet to foreach loop the foreach will execute against each of the outputs of the proceeding cmdlet. In other words if our get-mailboxserver returns five objects, we will execute the foreach loop five times. This pipelining the output of a cmdlet into foreach (foreach-object) is one of the most common things that I end up doing when writing any script. Within the loop itself we are doing another pipeline. In this case we are taking the output from the get-mailboxserver cmdlet and using the identity property ($_.X Means use the X property from the object coming down the pipe) to search for all mailboxes that are located on that server. Once we have gathered up that set of users we pipeline it into the export-mailbox cmdlet and use that cmdlet to do the work. The export-mailbox cmdlet we have setup will simply search all of the mailboxes we give it off the pipeline for any messages that have the specified keywords in the subject and then move them to our target mailbox into a folder called "Deleteme". Now the big question that some people might be asking is why I didn't just use a much simpler Powershell command to do this process. Get-mailbox -resultsize unlimited | export-mailbox –subjectkeywords "Bad Message" -targetmailbox <newmailbox> -targetfolder Deleteme –deletecontent –confirm:$false After all doesn't that accomplish the same thing? The answer is yes and no. If you have a small environment with let's say less than 5000 users or so then the shorter command above is perfect for you. If you have an environment that is larger than that then you will want to use the foreach command to streamline the process. The reason for this is that the export-mailbox cmdlet is a multi threaded cmdlet so it is going to force the aggregation of all of the mailbox objects before it begins operating on them. (To identity if a cmdlet is a multi threaded cmdlet look for the –maxthreads switch.) Since that is a limitation of the current cmdlets we are working with in a large environment operating on the users on a per server basis will get things started faster (since we won't have to wait on AD to return all 50k objects to get started) and will be less resource intensive. Hopefully I have accomplished my goal of showing everyone a little bit more about Powershell and sharing a script that will be of use more than once for people. Again I would like to solicit feedback from you the reader on how you liked this article and if there is anything you would like to see turned into a script. Otherwise I will just have to make something up for my next post and who knows what I will think of. - Matthew Byrd

Updated Jul 01, 2019
Version 2.0

14 Comments

  • Well heres my PowerShell attempt. As Exchange 2007 is so boring, it just works, we stopped monitoring it, but we were getting worried that sometimes it had stopped working, where as in fact the complaining users (including me) had in fact received no mail.

    So I wrote this scipt that logs some performance counters from the SMTP send and receive connectors and the sizes, in MB, of users mail boxes all redirected to a file call DailyFile.log. The file is ASCII as opposed to Unicode as I use a little utility (blat.exe) to e-mail the report to the administrators (Unicode didn't work).

    The script is actually generated by a .CMD file and "echo...> file.ps1", the .CMD file being scheduled to run at 6am & 6pm.

    I am sure someone could improve this, I am sure I saw a PowerShell e-mailer somewhere that talks to a SMTP port and the performance counter reading bit is a bit heavy....but it works and I know Exchange is working fine.


    $c1 = new-object System.Diagnostics.PerformanceCounter("MSExchangeTransport SmtpReceive","Messages Received Total","from POP3 forwarder")
    $c2 = new-object System.Diagnostics.PerformanceCounter("MSExchangeTransport SmtpSend","Messages Sent Total","to FastHosts")
    $c3 = new-object System.Diagnostics.PerformanceCounter("MSExchangeTransport SmtpReceive","Bytes Received Total","from POP3 forwarder")
    $c4 = new-object System.Diagnostics.PerformanceCounter("MSExchangeTransport SmtpReceive","Message Bytes Received Total","from POP3 forwarder")
    $c5 = new-object System.Diagnostics.PerformanceCounter("MSExchangeTransport SmtpSend","Message Bytes Sent Total","to FastHosts")
    $c1, $c2, $c3, $c4, $c5 | ft CounterName, RawValue -Autosize | out-file "dailyfile.log" -encoding ascii
    Get-MailboxStatistics | select-object DisplayName, ItemCount, TotalItemSize | Sort-Object -property TotalItemSize -descending | ForEach-Object -process { $_.TotalItemSize = [system.math]::round(($_.TotalItemSize)/1024.0/1024.0, 2); $_ } | ft -autosize | out-file "dailyfile.log" -encoding ascii -append
    exit
  • Desperately needed (due to so few if any "complete" examples that "just work") are examples of integrating exchange + powershell with ASP.NET.  Large and small organizations both have reasons for needing admin related exchange information or methods in web pages, and months after exchange 2007's release there is nearly no information about it vs the tons of examples in 2003 with CDOEXM, WMI, etc.
    For example, we probably aren't going to give our first line help desk powershell access.  But we want them to be able to check the status of a user's mailbox, find out their quota and quota status, what their settings are, folder sizes, get their activesync recovery password, etc.  All these things are in powershell but how about some examples and best practices for putting this into ASP.NET?  
    There ought to be a couple examples either here, or on Technet, and maybe in the SDK: perhaps a single user report page, an interactive table type page with a drilldown form, and a page carrying out a method/operation and returning the results, along with showing proper error handling from powershell into ASP.NET.  Best practices for running with different credentials than the web page calling user.  Best practices for manipulating the data from powershell into either raw HTML or into ASP.NET controls.
    Right now, the lack of these sort of examples makes powershell a big stumbling block in this scenario compared with a .NET based API, even though interactively, it is relatively easy to use and addictive.
  • The 'for-each' looping definitely makes transition and deployment processes easier.  My team and I have designed a few Powershell one-liners to move mailboxes and set UM settings.  Below is two examples:

    Move-Mailboxes using a CSV File: (This can run in multiple Powershell sessions)

    Import-CSV Users.csv | For-Each {Move-Mailbox -Identity $_.UserAlias -TargetDatabase $_.Database -BadItemLimit:10 -Confirm:$False}

    UM Enable Mailboxes with Extension Numbers:

    Import-CSV Users.csv | foreach {Enable-UMMailbox -Identity $_.UserAlias -UMMailboxPolicy 'San Leandro UM Dial Plan Default Policy' -Extensions $_.ExtNo -Pin '1234' -PinExpired $false}

    One-liners are going to save a mass amount of time for Administrators, Consultants, and etc..
  • Thanks for Matthew.  This is one of the reasons I really do love PowerShell.  This scenario plays out more often then you'd think, especially at large companies.  I've gotten into having a folder for Exmerge with a pre-configured ini file on all servers to yank stuff, so being able to do this with PoSh is great.  

    I really wish we could remove the "recall" message though from Outlook as it just doesn't work and really confuses people.