By default the assemblies necessary to complete the discovery and monitoring are installed under the \%ProgramData\% directory. You can set this to any valid path to install them in a different location.
IntervalSeconds
int
$Config/IntervalSeconds$
Interval Seconds
The interval that this Monitor is run
ScriptTraceEnabled
int
$Config/ScriptDebugEnabled$
Script Trace Enabled
To enable detailed logging of this Monitor set this to 1, setting it to 0 will disable logging. The logs are sent to the Windows Event Log and can be found with the other Operations Manager logs.
Source Code:
<UnitMonitorType ID="TeamFoundationServer2017.TfsPowerShellMonitor" Accessibility="Public">
<MonitorTypeStates>
<MonitorTypeState ID="Error" NoDetection="false"/>
<MonitorTypeState ID="Success" NoDetection="false"/>
</MonitorTypeStates>
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="ClassName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="ObjectPath" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="BaseInstallPath" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="IntervalSeconds" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="ScriptDebugEnabled" type="xsd:integer"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="BaseInstallPath" Selector="$Config/BaseInstallPath$" ParameterType="string"/>
<OverrideableParameter ID="IntervalSeconds" Selector="$Config/IntervalSeconds$" ParameterType="int"/>
<OverrideableParameter ID="ScriptTraceEnabled" Selector="$Config/ScriptDebugEnabled$" ParameterType="int"/>
</OverrideableParameters>
<MonitorImplementation>
<MemberModules>
<DataSource ID="DataSource" TypeID="System!System.SimpleScheduler">
<IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds>
<SyncTime/>
</DataSource>
<ProbeAction ID="Script" TypeID="Windows!Microsoft.Windows.PowerShellPropertyBagProbe">
<ScriptName>TfsMonitor.ps1</ScriptName>
<ScriptBody><Script>param($baseInstallPath, $className, $objectPath, $scriptDebugEnabled)
###
# This monitor will check the health of a specific Tfs component via a C# Class
# To insure errors don't go on noticed $ErrorActionPreference will be set to Stop.
###
# $baseInstallPath - An override location to install the binary - if not provided a default will be used, see Compute-InstallPath
# $className - The name of the class (from with in ManagementPack namespace/assembly) that should be used for this discovery
# $scriptDebugEnabled - A flag to control if debug information should be logged
# Errors and Warnings are always logged
#
# Verifies that the binary files has been placed in the provided install directory
# It can fail if we can't a lock using the mutex for the install path
function Validate-Install
{
param ([string] $installPath)
## Need void here to prevent powershell from adding a space to the caller outout
[void] [System.Threading.Mutex]$mutex
[bool] $gotMutex = $false
try
{
[string] $mutextName = ("Global\{0}" -f $installPath.Replace("\",";"))
Log-Message -message ("Creating Mutex: {0}" -f $mutextName) -severity $Script:severityInfo
[bool]$wasCreated = $false
$mutex = New-Object System.Threading.Mutex($true, $mutextName, [ref] $wasCreated)
if ($wasCreated)
{
Log-Message -message "Created Mutex" -severity $Script:severityInfo
$gotMutex = $true
}
else
{
Log-Message -message "Waiting for up to 3 secs for Mutext" -severity $Script:severityInfo
try
{
$gotMutex = $mutex.WaitOne(3000)
}
catch [System.Threading.AbandonedMutexException]
{
## We have no problem just taking over an Abandoned Mutex
Log-Message -message "Mutex was Previously Abandoned" -severity $Script:severityInfo
#
# Does the work of Verifying that the binaries files have been placed.
# Assumes the caller already obtained the needed Mutext to prevent raise conditions
function Validate-Install-UnderMutex
{
param ([string] $installPath)
#
# Adds information about the current user to the log
function Log-User
{
$owner = (Get-WmiObject win32_process | Where-Object {$_.ProcessId -eq $pid }).getowner()
#
# Calls ManagementPack.exe and logs the response. Additionally parse out the log and returns the pay load
# $forDiscovery should be $true when running a Discovery and $false for monitors
function Run-ManagementPack
{
param ([string] $baseInstallPath, [bool] $forDiscovery, [string] $className, [string] $params)
[int] $severity = $Script:severityInfo
[string] $message = "Exit Code $exitCode from Calling $command $([System.Environment]::NewLine)$response"
# $exitCode = 0 - No errors, log severity info
# $exitCode = 1 - At least one error and we were able get some data, log severity Error
# $exitCode = 2 - Failed to get data, throw
# $exitCode = Anything Else - Unexpected error, throw
if (($exitCode -eq 0) -or ($exitCode -eq 1))
{
if ($status -gt 0)
{
$severity = $Script:severityError
}
Log-Message -message $message -severity $severity
}
else
{
throw [Exception] $message
}
# At this point response should hold a log and a payload, return the payload
[string] $logBarrior = "**[[<<END OF LOG>>]]**"
[int] $index = $response.IndexOf($logBarrior)
if ($index -lt 0)
{
throw [Exception] "Failed to find $logBarrior in response: $response"
}
#
# Takes a string that could be very long and breaks it into smaller chunks
function Get-Chunks
{
param ([string] $message)
$chunkSize = 20480 #20K
$result = @{}
#
# Wrapper to obtain and cache the value to MOM COM Script API
function Get-MomScriptApi
{
if ($Script:MomSciptApi -eq $null)
{
$Script:MomSciptApi = new-object -comObject 'MOM.ScriptAPI'
}
return $Script:MomSciptApi
}
#
# Log that we are here and validate our input
Log-Message -message ("First Param baseInstallPath:{0}" -f $baseInstallPath) -severity $Script:severityInfo