Capturing standard EXE output in PowerShell

I’m sure there’s a better way of doing this, but here’s how I captured the output of SQL’s bcp.exe in order to email it and the CSV that we were creating automatically on a schedule overnight.

I ended up piping the executable to a temp file, and then grabbing the contents of that same temp file to populate the email body. Here are the lines in question:

&$exe $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 > c:\temp\out.txt

$bcpOutput = Get-Content c:\temp\out.txt | Out-String
Remove-Item c:\temp\out.txt

Here’s the script in its entirety:

# This needs to be set, otherwise Send-MailMessage doesn’t have a server to send through$PSEmailServer ="mailserver.local"
$PSEmailServer ="mailserver.local"

$emailRecipient = "Daniel <daniel@contoso.com>"

# Some date and filename related stuff. This part's not important
$d = Get-Date
$yesterday = $d.AddDays(-1)
$filepath = "C:\Exports\SQL Export\"
$filename = "SQL-DailyExport-{0}.{1}.{2}.csv" -f $yesterday.Day,$yesterday.Month,$yesterday.Year
$fullpath = $filepath + $filename

# Path to the executable
$exe = "C:\Program Files\Microsoft SQL Server\90\Tools\Binn\bcp.exe"

# arguments, as required
$arg1 = "DBName..ViewName"
$arg2 = "out"
$arg3 = $fullpath
$arg4 = "-c"
$arg5 = "-t,"
$arg6 = "-T"

# Run the executable with the arguments, and pipe the STDOut output to a text file
&$exe $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 > c:\temp\out.txt

# An intro line for the body of the email
$bcpOutput = "SQL daily export process information: `r`n"

# Get the contents of the temporary file that we created earlier
$bcpOutput += Get-Content c:\temp\out.txt | Out-String

# Remove the temporary file
Remove-Item c:\temp\out.txt

# Build up our subject line. This part's not important
$subjectText = "SQL export for {0}/{1}/{2}" -f $yesterday.Day,$yesterday.Month,$yesterday.Year

# Append some more information to the end of the body text. Again, not important
$bcpOutput += "`r`nThe output file is attached, and can also be found in \\sql\Exports"

# Send the email with the file attached and the body text as we've built it up
Send-MailMessage -From "SQL Export <Process@sql.server>" -To $emailRecipient -Subject $subjectText -Body $bcpOutput -Attachments $fullpath

This results in an email that contains the output from bcp.exe as well as the actual SQL export file attached to the email.

Using bcp.exe from a PowerShell script

Recently had to convert a batch file that calls bcp.exe to a more involved PowerShell script. The script exports the contents of a view to CSV format.

I originally had some issues getting the command line arguments to work, but here’s how I got it working:

$fullpath = c:\temp\out.csv

$exe = "C:\Program Files\Microsoft SQL Server\90\Tools\Binn\bcp.exe"
$arg1 = "DBName..ViewName"
$arg2 = "out"
$arg3 = $fullpath
$arg4 = "-c"
$arg5 = "-t,"
$arg6 = "-T"

&$exe $arg1 $arg2 $arg3 $arg4 $arg5 $arg6

Batch convert images to B&W with PowerShell and ImageMagick

I knocked together this PowerShell script today to batch-convert 600+ staff photo images to B&W.

2012-08-02 14-07-00_000101

You’ll require the following items installed and in your path before this script will work:

  1. ImageMagick. I used the normal version, not one of the alternate ones.
  2. jhead: exif jpeg header manipulation tool. This requires ImageMagick in order to work.

ImageMagick does the actual conversion to B&W. I played with converting to pure grayscale, but it didn’t look good. This method instead strips out all colour information by setting saturation to zero:

convert.exe {sourcefile} -modulate 100,0 {destinationfile}

An issue I came across then is that ImageMagick doesn’t update the embedded JPG thumbnail. This issue almost stumped me, but then I came across this great little tool called jhead. Amongst other things, jhead can regenerate the JPG thumbnail (only if one existed originally):

jhead.exe -rgt {filename}

Tying it all together is PowerShell:

<#
    .SYNOPSIS
    Generate B&W versions of images 
   
       Daniel Streefkerk
    
    THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE 
    RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
    
    Version 10, 02/08/2012
    
    .DESCRIPTION
    
    This script runs though a folder full of images and creates B&W versions of said images
    
    IMPORTANT NOTE: This script requires ImageMagick and jhead. For more information, see my blog
    
    .PARAMETER RootFolder
    The folder within which to process images

    .PARAMETER Recursive
    Also scan subfolders? This is enabled by default
    
    .EXAMPLE
    Process all images in c:\temp
    .\Generate-BWImages.ps1 -RootFolder c:\temp
    
    #>

Param(
  [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false,HelpMessage='The folder that contains the photos')]
  [String]
  #[ValidateScript({Test-Path $_ -PathType 'Container'})] 
  $RootFolder,
  [Parameter(Position=1,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Recurse through all subfolders?')]
  [bool]
  $Recursive=$true
)

cls

# Change these if necessary
$fileExtensions = "*.jpg"
$fileNameSuffix = "_bw" # the text to be appended to the file name to indicate that it has been modified

$files = $null;
$fileCount = 0

# Check if the root folder is a valid folder. If not, try again.
if ((Test-Path $RootFolder -PathType 'Container') -eq $false) {
    Write-Host "'$RootFolder' doesn't seem to be a valid folder. Please try again" -ForegroundColor Red
    break
}

# Get all image files in the folder
if ($Recursive) {
    $files = gci $RootFolder -Filter $fileExtensions -File -Recurse
} else {
    $files = gci $RootFolder -Filter $fileExtensions -File
}
# If there are no image files found, write out a message and quit
if ($files.Count -lt 1) {
    Write-Host "No image files with extension '$fileExtensions' were found in the folder '$RootFolder'" -ForegroundColor Red
    break
}

# Loop through each of the files and process it
foreach ($image in $files) {
    $newFilename = $image.DirectoryName + "\" + $image.BaseName + $fileNameSuffix + $image.Extension
    $imageFullname = $image.FullName

    write-host "Processing image: $imageFullname" -ForegroundColor Green
    & convert.exe $image.FullName -modulate "100,0" $newFilename
    write-host "Updating embedded thumbnail for: $newFilename" -ForegroundColor Green
    & jhead.exe -rgt $newFilename

    $fileCount++
}

Write-Host "$fileCount images processed" -ForegroundColor Yellow

Updating multiple Exchange 2010 Receive Connectors at once

Although I haven’t really delved into it properly yet, PowerShell is an awesome tool to have in your toolkit when managing Exchange. When we deployed Exchange 2010, I supplied the sysadmins in our other offices with a list of PowerShell commands to configure their servers appropriately. This saved a massive amount of time and hassle.

I recently had to bump up the incoming message size limit on our incoming SMTP (from Messagelabs) Receive Connectors to 26MB on five different servers.

Back in the days of Exchange 2003, embarrassingly for me, not that long ago; the same task would have required much mouse clicking and searching for settings. Now, in Exchange 2010, since I’ve followed a naming convention for the connectors across all of the servers, it’s easy to retrieve all of the relevant ones at once:

[PS] C:\>Get-ReceiveConnector |Where-Object  {$_.Identity -like "*Messagelabs*"} | fl identity

Identity : SITE1-MAIL-CAS1\Incoming SMTP from Messagelabs SITE1-MAIL-CAS1 Identity : SITE1-MAIL-CAS2\Incoming SMTP from Messagelabs SITE1-MAIL-CAS2 Identity : SITE2-MAIL\Incoming SMTP from Messagelabs SITE2-MAIL Identity : SITE3-MAIL\Incoming SMTP from Messagelabs SITE3-MAIL Identity : SITE4-MAIL\Incoming SMTP from Messagelabs SITE4-MAIL Identity : SITE5-MAIL\Incoming SMTP from Messagelabs SITE5-MAIL

Updating the MaxMessageSize on all of the connectors is then as simple as issuing this one liner:

Get-ReceiveConnector | Where-Object  {$_.Identity -like "*Messagelabs*"} | Set-ReceiveConnector -MaxMessageSize 26mb

The great thing with many PowerShell commandlets is that you can “get” the objects you’re after, and then pipe the results into the command that makes the changes.

This is just a really simple example, and most experienced Exchange admins would probably laugh at the fact that I’ve documented something this simple – but I just wanted to highlight how easy it is to make “bulk” changes with PowerShell and EMS, the Exchange Management Shell.

Remember that you might want to run commands such as this with the “-WhatIf” switch just to check what’s going to be affected before going ahead:

[PS] C:\>Get-ReceiveConnector |Where-Object  {$_.Identity -like "*Messagelabs*"} |Set-ReceiveConnector -MaxMessageSize 26mb -WhatIf
What if: Configuring Receive connector "SITE1-MAIL-CAS1\Incoming SMTP from Messagelabs SITE1-MAIL-CAS1".
What if: Configuring Receive connector "SITE1-MAIL-CAS2\Incoming SMTP from Messagelabs SITE1-MAIL-CAS2".
What if: Configuring Receive connector "SITE2-MAIL\Incoming SMTP from Messagelabs SITE2-MAIL".
What if: Configuring Receive connector "SITE3-MAIL\Incoming SMTP from Messagelabs SITE3-MAIL".
What if: Configuring Receive connector "SITE4-MAIL\Incoming SMTP from Messagelabs SITE4-MAIL".
What if: Configuring Receive connector "SITE5-MAIL\Incoming SMTP from Messagelabs SITE5-MAIL".

Failing Exchange Transport Service after Policy Patrol 8 upgrade

Just had an issue on one of my Exchange 2010 (SP2 RU3) CAS servers when upgrading Policy Patrol from v7.x to v8.0. The install/upgrade completed successfully, but I noticed the following errors in the Application log afterwards:

Error 1052, MSExchange Extensibility
Failed to create agent factory for the agent 'Policy Patrol Email Edge' with error 'Failed to create type 'RedEarthSoftware.PolicyPatrol.Email.Agent.EdgeTransportFactory' from assembly 'C:\Program Files (x86)\Red Earth Software\Policy Patrol Email\Agent\PolicyPatrol.Email.Agent.dll' due to error 'Invalid agent assembly path.'.'. Please verify the corresponding transport agent assembly and dependencies with correct version are installed.

Error 16023, MSExchangeTransport
Microsoft Exchange couldn't start transport agents. The Microsoft Exchange Transport service will be stopped. Exception details: Failed to create type 'RedEarthSoftware.PolicyPatrol.Email.Agent.EdgeTransportFactory' from assembly 'C:\Program Files (x86)\Red Earth Software\Policy Patrol Email\Agent\PolicyPatrol.Email.Agent.dll' due to error 'Invalid agent assembly path.'. : Microsoft.Exchange.Data.ExchangeConfigurationException: Failed to create type 'RedEarthSoftware.PolicyPatrol.Email.Agent.EdgeTransportFactory' from assembly 'C:\Program Files (x86)\Red Earth Software\Policy Patrol Email\Agent\PolicyPatrol.Email.Agent.dll' due to error 'Invalid agent assembly path.'. ---> System.ArgumentException: Invalid agent assembly path.
   --- End of inner exception stack trace ---
   at Microsoft.Exchange.Data.Transport.Internal.MExRuntime.FactoryTable.CreateAgentFactory(AgentInfo agentInfo)
   at Microsoft.Exchange.Data.Transport.Internal.MExRuntime.FactoryTable..ctor(IEnumerable agents)
   at Microsoft.Exchange.Data.Transport.Internal.MExRuntime.RuntimeSettings..ctor(MExConfiguration config, String agentGroup)
   at Microsoft.Exchange.Data.Transport.Internal.MExRuntime.MExRuntime.Initialize(String configFile, String agentGroup, Boolean isBridgeHead, String installPath)
   at Microsoft.Exchange.Transport.Extensibility.AgentComponent.Load()

The Microsoft Exchange Transport service was failing because it seemed that the transport agent configuration hadn’t been updated as part of the upgrade. This was caused by issues with the Exchange Management Shell on the server in question. I found this in the Policy Patrol install logs:

ERROR:The Windows PowerShell snap-in 'Microsoft.Exchange.Management.PowerShell.Admin' is not installed on this machine.
ERROR:The Windows PowerShell snap-in 'Microsoft.Exchange.Management.PowerShell.E2010' is not installed on this machine.

Luckily, I had another server to reference, so this is what I did to resolve the problem:

  1. Stopped the Policy Patrol services
  2. Opened a new EMS (Exchange Management Shell) window and did this:
    1. Uninstall-TransportAgent “Policy Patrol Email Edge”
    2. Uninstall-TransportAgent “Policy Patrol Email Hub”
  3. Closed the EMS window, and restarted the Microsoft Exchange Transport service
  4. Opened a new EMS window, and did the following:
    1. Install-TransportAgent -Name “Policy Patrol Email Edge” -TransportAgentFactory “RedEarthSoftware.PolicyPatrol.Email.Engine.Ex2007.EdgeAgentFactory” -AssemblyPath “C:\Program Files (x86)\Red Earth Software\Policy Patrol Email\PolicyPatrol.Email.Engine.Ex2007.Sink.dll”
    2. Install-TransportAgent -Name “Policy Patrol Email Hub” -TransportAgentFactory “RedEarthSoftware.PolicyPatrol.Email.Engine.Ex2007.PostCatAgentFactory” -AssemblyPath “C:\Program Files (x86)\Red Earth Software\Policy Patrol Email\PolicyPatrol.Email.Engine.Ex2007.Sink.dll”
  5. Restarted the Microsoft Exchange Transport service
  6. Opened a new EMS window
    1. Enable-TransportAgent “Policy Patrol Email Edge”
    2. Enable-TransportAgent “Policy Patrol Email Hub”
  7. Restarted the Microsoft Exchange Transport service
  8. Started the Policy Patrol services

That’s it. No more issues loading the transport agents, and no more crashing Microsoft Exchange Transport service.

Update – 10/07/2012: Added some info about the cause of the problem.