get-process  |  {$ -match "win"}

Sample output is shown below. The above means “get all the process objects on this computer” and pipe that collection of objects to a where/filter where we only want to see processNames that contain the letters “host”.

Example output:

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     75       8      796       3680    46             744 wininit
    152       8     1548       8072    54             776 winlogon

“Match” actually does a RegEx (regular expression match, so you can also do this: Find me all process that contain a “t” followed by exactly three characters, followed by the letters “host”:

PS C:\Users\Neal> get-process  | where {$ -match "t\w{3}host"}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    181      17     9696      10796   693            7212 taskhost
    290      51    10992      17588   301     0.33   2860 taskhostex

How does this relate to BizTalk? We often deal with windows services. Suppose you are wondering if there is a DTC task started on the computer, but you don’t remember the exact service name.
Run something like this:

get-service | where {$ -match "dtc"}

Of course, if you remember the exact service name, you can enter a shorter command:

get-service -Name msdtc

On my home PC, both the above return the same result:

Status   Name               DisplayName
------   ----               -----------
Stopped  MSDTC              Distributed Transaction Coordinator

Suppose you wanted to show all processes that are stopped:

get-service | where {$_.Status -eq 'Stopped'}

Or show me only services that contain the “dtc” in the name that are stopped:

get-service | where {$_.Status -eq 'Stopped' -and $_.Name -match 'dtc'}

TIPS: Don’t forget to use -EQ instead of the = or == sign. This is not C#. Also don’t forget to put the – in front of -and, -eq, and -match.

To know what properties you can query with the -eq and the -match filter, do the following:

get-service | get-member

or the abbreviation for get-member “gm” is often used

get-service | gm

In this case, you are piping the output of the “get-service” cmdlet to the “get-member” cmdlet, which displays returns all the property, methods, and events.


Just to complete the thought, you can see just the properties by doing either of the following:

get-service | get-member  | where-object  {$_.MemberType -eq "Property"}

get-service | get-member -MemberType Properties


Just for the sake of completeness, you might also want to sort your data by some particular property. Just pipe what we learned above to the “sort-object” and tell it which property (and optionally which order) to sort.

get-service | where {$ -match "dtc"} | sort-object Status
get-service | where {$ -match "dtc"} | sort-object Status -desc 

You can set this up in Task Scheduler to run every x minutes and send you an email when there are any errors in the Application Event Log or the System Event Log.

There is a section in the code where you can create more user-friendly messages for complex or confusing error messages. It also allows you to filter out certain known errors that you get over-and-over, but do not want to fix. (For example, we saw an error from a tool called “Monitis” every hour or so, so we didn’t want emails to go out on those types of known and common errors. We were primarily interested in BizTalk errors. (Look for the code: “Where-Object {$_.Message -notlike ‘Monitis*'” to set up your own filters to exclude certain types of errors).

# EventLog Monitoring and reporting script
# Neal Walters - 06/18/2013 
# Modeled after DiskMonitor.ps1 
#  11/08/2013 - added Select-Object -Propert to only return desired columns (originally 10/17/2013)

[string[]] $users = "","","" # List of users to email your report to (separate by comma) or use a distribution list 

$TraceFilename = "E:\Scripts\EventLogExtract_Trace.txt"
$fromemail = ""
$server = "" #enter your own SMTP or Relay server DNS name / IP address here
$minutes = $args[0]
$environment = $args[1] 
$skipmail = $args[2] 

#allow this to be run 
if ($minutes -eq $null)
     $minutes = 10
if ($environment -eq $null)
     $environment = "New-BT-Cluster" 

$activeClusterName = (Get-WMIObject Win32_ComputerSystem -ComputerName BIZTALK2013).Name
Write-Host "`$activeClusterName=$activeClusterName"
$computerName = get-content env:computername
if ($activeClusterName -eq $computerName)
   $activeNodeMessage = "(Is-Active-Node)" 
   $activeNodeMessage = "(Is-NOT-Active-Node)" 

#Write-Host $minutes

$traceDateTime = get-date
Write-Host "$traceDateTime Starting Application EventLog"

#$tableFragment = Get-Eventlog -log application -after ((get-date).addMinutes(-120)) -EntryType Error, Warning | ConvertTo-Html -fragment
#multiple $minutes by negative 1 to go back x minutes 
$getEventLog = Get-Eventlog -log application -after ((get-date).addMinutes($minutes*-1)) -EntryType Error 
$traceDateTime = get-date
Write-Host "$traceDateTime Completed Application EventLog count=$($getEventLog.Count)"
Add-Content $TraceFilename "$traceDateTime TRC20 Completed Application EventLog count=$($getEventLog.Count)"
Write-Host "$traceDateTime Starting System EventLog"
$getEventLogSys = Get-Eventlog -log system -after ((get-date).addMinutes($minutes*-1)) -EntryType Error 
$traceDateTime = get-date
Write-Host "$traceDateTime Completed System EventLog count=$($getEventLogSys.Count)"
Add-Content $TraceFilename "$traceDateTime TRC21 Completed System EventLog count=$($getEventLogSys.Count)"

# list of events to exclude (based on text found in the message 
# NOTE: Powershell uses grave-accent mark as line continuation character, but when I split onto 3 lines 
#       I seemed to get emails that had the excluded phrases. 
$getEventLogFiltered = $getEventLog | Where-Object {$_.Message -notlike 'Monitis*' -and $_.Message -notlike '*MQQueueDepthMonitor.exe*' -and $_.Message -notlike '*The local computer may not have the necessary registry*' }
Add-Content $TraceFilename "$traceDateTime TRC22 Filtered EventLog count=$($getEventLog.Count)"
#to only select certain columns, use Select-Object -Property and list the property/columns 									   
$getEventLogColumns = 	$getEventLogFiltered	| Select-Object -Property TimeGenerated,Source,Message,EntryType,EventID

$getEventLogWithSelectedColumns = 	$getEventLogFiltered |   
    Select-Object -Property TimeGenerated,Source,@{name='FriendlyDescr';expression={Message}},Message,EntryType,EventID
#$getEventLogSysWithSelectedColumns = 	$getEventLogSys |   
#    Select-Object -Property TimeGenerated,Source,@{name='FriendlyDescr';expression={Message}},Message,EntryType,EventID

if ($getEventLogFiltered -ne $null -or $getEventLogSys -ne $null) 
        $needToSendEmail = "Y" 
		# This is where you can put short friendly Messages that the average guy can understand. 
		foreach ($item in $getEventLogWithSelectedColumns) 
		     $item.FriendlyDescr = "N.A."
			 #switch -wildcard ($item.Message) 
			 #  {
			 #     "*The file exists*" {$Item.FriendlyDescr="Duplicate File";break}
			 #  }
		     if ($item.Message.Contains("SQL Timeout")) 
				   $item.FriendlyDescr = "SQL-Timeout"
      Write-Host "No EventLog Rows selected" 

$head = @"
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Frameset//EN” “”&gt;
<html><head><title>Unmigrated Systems Report</title><meta http-equiv=”refresh” content=”120? />
<style type=”text/css”>
body {
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;

#report { width: 1400px; }

border-collapse: collapse;
border: none;
font: 10pt Verdana, Geneva, Arial, Helvetica, sans-serif;
color: black;
margin-bottom: 10px;
width: 1400px;

table td{
font-size: 12px;
padding-left: 0px;
padding-right: 20px;
text-align: left;

table th {
font-size: 12px;
font-weight: bold;
padding-left: 0px;
padding-right: 20px;
text-align: left;

h2{ clear: both; font-size: 130%;color:#354B5E; }

clear: both;
font-size: 75%;
margin-left: 20px;
margin-top: 30px;

p{ margin-left: 20px; font-size: 12px; }

table.list{ float: left; }

table.list td:nth-child(1){
font-weight: bold;
border-right: 1px grey solid;
text-align: right;

table.list td:nth-child(2){ padding-left: 7px; }
table tr:nth-child(even) td:nth-child(even){ background: #BBBBBB; }
table tr:nth-child(odd) td:nth-child(odd){ background: #F2F2F2; }
table tr:nth-child(even) td:nth-child(odd){ background: #DDDDDD; }
table tr:nth-child(odd) td:nth-child(even){ background: #E5E5E5; }
div.column { width: 320px; float: left; }
div.first{ padding-right: 20px; border-right: 1px grey solid; }
div.second{ margin-left: 30px; }
table{ margin-left: 20px; }


#seems like we cannot put -head and -fragment on the same ConvertTo-Html statemnet 
#$tableFragment = $getEventLogWithSelectedColumns | 
#    ConvertTo-Html -Fragment TimeGenerated,EntryType,Source,FriendlyDescr,Message,EventID 
$tableFragmentTemp = $getEventLogWithSelectedColumns | 
    ConvertTo-Html -Fragment TimeGenerated,EntryType,Source,FriendlyDescr,Message,EventID 
$tableTempFragments = "<h2><font color='yellow'>Application Event Log<font></h2>" + $tableFragmentTemp 

if ($getEventLogSys -ne $null) 
$tableFragmentTempSys = $getEventLogSys | 
    ConvertTo-Html -Fragment TimeGenerated,EntryType,Source,FriendlyDescr,Message,EventID 

$tableTempFragments = $tableTempFragments + "<h2><font color='yellow'>System Event Log<font></h2>" + $tableFragmentTempSys

$tableFragment = ConvertTo-HTML -head $head -PostContent $tableTempFragments 

# Production red-alert colors 
if ($environment -eq "PROD")
	$background = "Red" 
	$backgroundSolid = "DarkRed"
	$foreground = "White"
    # QA  colors 
	$background = "White" 
	$backgroundSolid = "Blue"
	$foreground = "Blue"

Write-Host "`$activeClusterName=$activeClusterName"

# assemble the HTML for our body of the email report.
$HTMLmessage = @"
<font color=""black"" face=""Arial, Verdana"" size=""3"">
<u><b>$Environment (Machine=$computerName $activeNodeMessage EventLog Error Report</b></u>
<br>This report was generated because of Errors found in the EventLog in the last reporting interval of $minutes minutes. 
<style type=""text/css"">body{font: .8em ""Lucida Grande"", Tahoma, Arial, Helvetica, sans-serif;}
ol{margin:0;padding: 0 1.5em;}
table{color:$foreground;background:$background;border-collapse:collapse;width:647px;border:5px solid $backgroundSolid;}
thead th{padding:1em 1em .5em;border-bottom:1px dotted #FFF;font-size:120%;text-align:left;}
thead tr{}
td{padding:.5em 1em;}
tfoot td{padding-bottom:1.5em;}
tfoot tr{}
<body BGCOLOR="$foreground">

# Set up a regex search and match to look for any <td> tags in our body. These would only be present if the script above found disks below the threshold of free space.
# We use this regex matching method to determine whether or not we should send the email and report.
$regexsubject = $HTMLmessage
$regex = [regex] '(?im)<td>'
$traceDateTime = get-date

# if there was any row at all, send the email
if ($skipmail -ne "Y")
	if ($regex.IsMatch($regexsubject)) 
	            $emailSubject = "$environment Event Log Error Report - Computer=$computerName"
                send-mailmessage -from $fromemail -to $users -subject $emailSubject  -BodyAsHTML -body $HTMLmessage -priority High -smtpServer $server
 		        Write-Host "$traceDateTime Email sent" 
                Add-Content $TraceFilename "$traceDateTime TRC95 Email Sent "
                Write-Host "$traceDateTime No email was needed because no Errors in Application EventLog in the last $minutes minutes" 
                Add-Content $TraceFilename "$traceDateTime TRC96 No Email Sent "
# End of Script

What I do is set up a .cmd or .bat file like this, and that is what I put in the scheduler. It simpley runs the Powershell and passes the necessary parms.
Make sure the first parm (10 in the example below) matches exactly how often you run the job in the task scheduler.

powershell -command "& 'E:\Scripts\EventLogExtract.ps1'" 10 NEW-BT-PROD

09/15/2014 14:46:28 Test
09/15/2014 14:47:02 Test
09/15/2014 14:48:02 Test
09/15/2014 14:49:02 Test

$Action1 = New-ScheduledTaskAction -Execute "I:\Scripts\TestClusteredTaskRS.cmd"
$Action2 = New-ScheduledTaskAction -Execute "E:\Scripts\TestClusteredTaskCW.cmd"
$DurationTimeSpan = New-TimeSpan -Minutes 1 
$DurationTimeSpanIndefinite	= ([TimeSpan]::MaxValue) 
$DurationTempTest = New-TimeSpan -Days 1000
$Trigger = New-ScheduledTaskTrigger -Once -At "00:01" -RepetitionInterval $DurationTimeSpan -RepetitionDuration $DurationTimeSpanIndefinite  

#Register-ScheduledTask -TaskName "RegularCalcTest2" -Action $action
#Register-ClusteredScheduledTask -TaskName "CalcTask01" -TaskType AnyNode -Action $Action1 -Trigger $Trigger -Cluster "YourClusterNameHere"
Register-ClusteredScheduledTask -TaskName "TestResourceSpecific" -TaskType ResourceSpecific  -Action $Action1 -Trigger $Trigger -Cluster "YourClusterNameHere" -Resource BizTalk2013
Register-ClusteredScheduledTask -TaskName "TestClusterWide"      -TaskType ClusterWide       -Action $Action2 -Trigger $Trigger -Cluster "YourClusterNameHere"

Contents of TestClusteredTaskCW.cmd

powershell -command "&amp; 'E:\Scripts\TestClusteredTaskCW.ps1'"


$TraceFilename = "E:\scripts\TestClusterWide_Trace.txt"
$currentDateTime = Get-Date 
Add-Content $TraceFilename "$currentDateTime Test" 
Write-Host "Completed, see `$TraceFilename=$TraceFilename" 

ClusterName : YourClusterNameHere
CurrentOwner :
Resource : 6f04356c-2f34-306b-9afb-a64e9556942f
TaskDefinition : MSFT_ScheduledTask (TaskName = “TestResourceSpecific”, TaskPath)
TaskName : TestResourceSpecific
TaskType : ResourceSpecific
PSComputerName :

ClusterName : YourClusterNameHere
CurrentOwner :
Resource :
TaskDefinition : MSFT_ScheduledTask (TaskName = “TestClusterWide”, TaskPath)
TaskName : TestClusterWide
TaskType : ClusterWide
PSComputerName :

Write “==================”
Write “Expanded:”
foreach ($item in Get-ClusteredScheduledTask )
Write-Host $item.TaskName

TaskName TaskType
——– ——–
TestClusterWide ClusterWide
TestResourceS… ResourceSpecific


Related Posts:


2. – error trying to Register-ClusteredScheduledTask

3. – how to you enable/disable a ClusteredScheduledTask?


I’m still cleaning up this blog post.  Here are some points to consider.


1. With ClusteredScheduledTasks there is no GUI.  When you do register a ClusteredScheduleTask using Powershell, it shows up in the GUI, but even then it is not reliable to use the GUI to change anything.   I tried changing few things, and they certainly did NOT sync between the two servers.

2. The problem with regular ScheduledTasks is that you have to maintain one TaskScheduler on each machine in the node.  Not too bad with two nodes, but if you have more it can be tedious.  Even with two nodes, everyone that is an admin has to be a stickler for keeping them in sync.
We thought about trying to write some code to keep them in sync, but looks like that could take several days.  In my opinion, you have the task, the triggers, and the actions all to keep in sync, and the triggers themselves have quite a few options.

At my client, we had two nodes, and decided to live with regular scheduled tasks for now.

3. You can always buy a third party tool.  Where I used to work, they had “VisualCron“.  I recently asked their sales person if they supported clustering, and rather than giving me a straight forward yes or no, and he sent me to a forum post which implies it can be done, but if it is so easy, there would be that much discussion about it.

4. We considered using SQL Agent as our task scheduler, but then we might have to change some of our code to work “cross-machine”, i.e. from the SQL machine over to the machine being monitored.  (Some of our schedule tasks kick of BizTalk jobs, and others are for monitoring EventLog, creation of extract files, MQ Depth, etc…)



I recently learned about the “View installed updates” link in Add/Remove programs.  It reveals the updates and BizTalk Cumulative Updates installed on your system.  Below are screen shots from Win 2008/R2.


Results – just scroll down and look for updates related to Microsoft BizTalk.  The Adapter Pack CU3 was temporarily uninstalled due an issue we are working on with Microsoft.  The following shows we are on CU5 (for the BizTalk Core product), and that CU6 is not installed.


In the place where I work, we have numerous SendPorts that we sometimes enable that create copies of BizTalk messages on the disk, in a directory called D:\MessageLog (with various sub-directories).

Rarely do we need to go back more than 2 or 3 days to see these files.

We have a Powershell script in place to help clean-up these files. We are a very high volume system, and we can end up with tens or hundreds of thousands of these messages in a few days.

#All files older than x days in D:\MessageLog
# $_.PsIsContainer tells you that you have a folder
GET-CHILDITEM D:\MessageLog -RECURSE| Where { $_.LastWriteTime.AddDays(2) -lt (GET-DATE) -and -not $_.psiscontainer} |% {REMOVE-ITEM $_.fullname -force}

This script could be scheduled by Windows Task Manager. To make things easier, I usually create a one line .bat or .cmd file like the one below. The scheduler simple executes the .cmd file.

powershell -command "& 'CleanupMessageLog.ps1'"

or if the Ampersand gives you errors, try

powershell -command ". 'e:\Projects\MyApp\CleanupMessageLog.ps1'"

NOTE: Put the folder name in double quotes if it contains any embedded spaces.

Compare to VBScript cleanup/delete files script.

I finally found the dream tool I have been wanting to work with the Application Event Log (for BizTalk, or for any other server related issues).

FSPRO Labs makes a tool called “Event Log Explorer”

How it is useful to me:

1) Most often, I have the same bogus error or warning occurs 1000s of times, and it’s hard to find the real errors.  With “Event Log Explorer” you can just bring up the filter, and exclude that specific warning, by several handy criteria.
2) It’s also useful for finding and showing just one specific type of error or warning (excluding all the others).
3) Both of the above are useful in determining when errors occur, how often they occur, and/or when the last time an error occurred, without filter through all the noise.

A sample screenshot follows:


Here’s a great checklist for a BizTalk Adminstrator to install BizTalk:

by Tord Glad Nordahl


Today, I recycled one of my host instances, and it didn’t start. Then I tried all of them, and several had issues. Finally, it dawned on me that I had changed the 64-bit config file:
“c:Program Files (x86)Microsoft BizTalk Server 2010BTSNTSvc64.exe.config”. The 32-bit host-instances were all starting fine, because it uses a different config file (BTSNTSvc.exe.config).

Check your web config file for malformed xml. (For example, make a copy called BTSNTSvc64.xml and open it in Internet Explorer to quickly find the error.)

I had something like this (to mailSettings close tags).

      <smtp from="">
        <network host="" password="" userName="" />
        <!-- formerly was: <network host="" password="" userName="" /> --> 
    </mailSettings>    </mailSettings> 

That’s it, just fix your web.config hopefully.

Error: Failed to load Group xxxx.BizTalkMgmtDb] data providers (in BTSAdmin Console)

Solution: Verify SQL Server is available, if not, start-it.

Screen shot below shows trying to connect to the SQL server; it might be on the same machine as BizTalk or it might be on another machine. The above error could also indicate a communication or maybe even a permission issue.

Open the SQL Config utility and start SQL server. The screen shot below shows how to start the SQL Configuration Manager:

The configuration utility shows which SQL services are started and stopped. Right click then select “Start” if it is not running:

I cannot count how many times I have got the error below:

It typically happens when you try to import a BizTalk MSI application fro, one machine and export it to another. What it implies is that that system you have exported from, and the system to which you are importing, having different settings on the Host. The first thing you normally do is go check your “trust levels” and whether the host is 32-bit or not. If that matches, then what do you do?

The secret is that this error can also indicate a problem with the adapters being related to the Host Name. In the above case, we had added the Open Source “NullAdapter” on the “from system”, and had installed the adapter on the “to system” as well. But we forgot to attach the adapter to the Host Instance, which was also called “NullAdapter” (we still don’t have good naming convention for our host instances at this site, as we inherited what was already here).

You can see this by looking at the adapters in BizTalk Admin Console:

Note in the above screen shot that the “NullAdapter” Host Name is missing from the list of “Host Names”, and in fact, there is no need for the other two “Host Names” to be there.

So first, right-click on the Adapter, click “new” then add the desired “Host Name”, as shown below. Make the new Host Name the default by checking the “Make this the default handler”. I actually forgot to that, so I got an error when I tried to delete the Host Name “BizTalkServerApplication” from the NullAdapter.

Then, associate the Adapter to the desired “Host Name”, by picking from the list of “Host Names”:

At the end, after deleting the two “Host Names” that should not have been there, the list of “Host Names” looks like this:

Remember again, I wouldn’t normally name the “Host Instance” the exact same name as the Adapter, but in this case it kind of made sense, because the “Null Adapter” is only used by us in a couple of odd places. How you should properly set up your Hosts could be the topic of another blog.