This is going to be an example of making a two state monitor to check for the existence of a file on an agent managed server.
If the file does not exist, we can run a recovery to copy the file there from a network location.
The context of this example is for a scenario, where we expect the OOMADS.MSI file (Active Directory Helper Objects) to be placed in a specific directoy on the agent: C:\Program Files\Microsoft Monitoring Agent\Agent\HelperObjects
However, if the agent is manually installed, we do not copy this file, it is copied only if the agent is pushed from the SCOM console. This might leave several active directory domain controllers without these necessary scripting objects deployed. Marnix wrote about this here: http://thoughtsonopsmgr.blogspot.com/2010/10/eventid-10-active-directory-helper.html
The AD management pack will automatically deploy this MSI if and when it is needed, but the MP expects the OOMADS.MSI to be in that specific directly above. Hence, this example.
We will start with a simple monitor example, which will use the Microsoft.Windows.TimedScript.TwoStateMonitorType. This monitor will run a VBscript, which we adapted from Pete Zerger’s script at http://www.systemcentercentral.com/opsmgr-creating-a-monitor-to-determine-if-a-file-exists-sample-script-and-tutorial/
Here is my customized version of Pete’s script:
'========================================================================== ' ' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 2009 ' ' NAME: DoesFileExist ' ' AUTHOR: Pete Zerger, MVP (Cloud and Datacenter Admin) ' DATE : 3/12/2012 ' ' COMMENT: Verifies a target file (including path) exists. ' Intended for use with OpsMgr two state script monitor. ' '========================================================================== OPTION EXPLICIT Call Main Sub Main() 'Declare Variables 'File-related variables Dim fso, folder, file, FilePath 'OpsMgr related variables Dim oArgs, oAPI, oBag Set oArgs = Wscript.Arguments ' Retrieve parameters folder = CStr(oArgs.Item(0)) file = CStr(oArgs.Item(1)) FilePath = folder & "\" & file WScript.Echo folder WScript.echo file WScript.Echo FilePath ' Instantiate File System Object Set fso = CreateObject("Scripting.FileSystemObject") ' Instantiate MOM API Set oAPI = CreateObject("MOM.ScriptAPI") Set oBag = oAPI.CreatePropertyBag() ' Verify the path to the file exists xists If (fso.FolderExists(folder)) Then 'Folder exists, submit property bag and continue Call oBag.AddValue("FolderExists","Yes") WScript.Echo "Folder exists" Else 'Folder does not exist, submit property bag and exit Call oBag.AddValue("FolderExists","No") Call oBag.AddValue("FileExists","No") WScript.Echo "Folder doesn't exist" oAPI.AddItem(oBag) Call oAPI.ReturnItems Exit Sub End If ' Verify the file exists If (fso.FileExists(FilePath)) Then 'File exists, submit property bag and exit Call oBag.AddValue("FileExists","Yes") oAPI.AddItem(oBag) Call oAPI.ReturnItems Else 'File does not exist, submit property bag and exit Call oBag.AddValue("FileExists","No") WScript.Echo "File doesn't exist" oAPI.AddItem(oBag) Call oAPI.ReturnItems Exit Sub End If End Sub
I will pass two parameters to this script – the directory and the filename I am looking for. This is a very reuseable monitor for other purposes:
I will create an expression for unhealthy, stating that if either the folder or file is missing, this is bad:
And expression for healthy requires BOTH to exist:
That part is quite simple – and will generate an alert with context:
Next, I will add a recovery task. This recovery will do two things. FIRST, it will include a condition detection, to ensure that for all the “unhealthy” monitors, we will only attempt a recovery action IF the required folder is present, just the file is missing. Then, it will call another VBscript which will copy the file locally for us.
The condition detection is a simple expression:
I could not find any good examples on the web for how to pass a property from a monitor, to a condition detection in a Recovery. There are lots of examples of passing the monitor property directly to a recovery script as a parameter, but none for a system.expression filter in a condition detection. The hardest part of this was figuring out the syntax for comparing the output of the variable property from the monitor:
StateChange/DataItem/Context/DataItem/Property[@Name=’FolderExists’]
Here is my recovery script… which is VERY basic:
Dim oAPI, fso Set oAPI = CreateObject("MOM.ScriptAPI") Call oAPI.LogScriptEvent("CopyFile.vbs",6002,0,"Starting copyfile script") set fso=CreateObject("Scripting.FileSystemObject") fso.CopyFile "\\scom01\AgentStuff\amd64\oomads.msi", "C:\Program Files\Microsoft Monitoring Agent\Agent\HelperObjects\" WScript.Quit
Now that we have that, putting it all together in a management pack is quite simple.
Here is the entire XML….
<?xml version="1.0" encoding="utf-8"?><ManagementPack ContentReadable="true" SchemaVersion="2.0" OriginalSchemaVersion="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <Manifest> <Identity> <ID>Demo.FileExists.Monitor</ID> <Version>1.0.0.3</Version> </Identity> <Name>Demo.FileExists.Monitor</Name> <References> <Reference Alias="Windows"> <ID>Microsoft.Windows.Library</ID> <Version>7.5.8501.0</Version> <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> </Reference> <Reference Alias="System"> <ID>System.Library</ID> <Version>7.5.8501.0</Version> <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> </Reference> <Reference Alias="SC"> <ID>Microsoft.SystemCenter.Library</ID> <Version>7.0.8433.0</Version> <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> </Reference> <Reference Alias="Health"> <ID>System.Health.Library</ID> <Version>7.0.8433.0</Version> <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> </Reference> </References> </Manifest> <Monitoring> <Monitors> <UnitMonitor ID="Demo.FileExists.Monitor.FileExists" Accessibility="Internal" Enabled="true" Target="Windows!Microsoft.Windows.Server.OperatingSystem" ParentMonitorID="Health!System.Health.ConfigurationState" Remotable="true" Priority="Normal" TypeID="Windows!Microsoft.Windows.TimedScript.TwoStateMonitorType" ConfirmDelivery="false"> <Category>AvailabilityHealth</Category> <AlertSettings AlertMessage="Demo.FileExists.Monitor.FileExists_AlertMessageResourceID"> <AlertOnState>Warning</AlertOnState> <AutoResolve>true</AutoResolve> <AlertPriority>Normal</AlertPriority> <AlertSeverity>Information</AlertSeverity> <AlertParameters> <AlertParameter1>$Data/Context/Property[@Name='FolderExists']$</AlertParameter1> <AlertParameter2>$Data/Context/Property[@Name='FileExists']$</AlertParameter2> </AlertParameters> </AlertSettings> <OperationalStates> <OperationalState ID="Success" MonitorTypeStateID="Success" HealthState="Success" /> <OperationalState ID="Error" MonitorTypeStateID="Error" HealthState="Warning" /> </OperationalStates> <Configuration> <IntervalSeconds>60</IntervalSeconds> <SyncTime /> <ScriptName>fileexists.vbs</ScriptName> <Arguments>"C:\Program Files\Microsoft Monitoring Agent\Agent\HelperObjects" "OomADs.msi"</Arguments> <ScriptBody>'========================================================================== ' ' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 2009 ' ' NAME: DoesFileExist ' ' AUTHOR: Pete Zerger, MVP (Cloud and Datacenter Admin) ' DATE : 3/12/2012 ' ' COMMENT: Verifies a target file (including path) exists. ' Intended for use with OpsMgr two state script monitor. ' '========================================================================== OPTION EXPLICIT Call Main Sub Main() 'Declare Variables 'File-related variables Dim fso, folder, file, FilePath 'OpsMgr related variables Dim oArgs, oAPI, oBag Set oArgs = Wscript.Arguments ' Retrieve parameters folder = CStr(oArgs.Item(0)) file = CStr(oArgs.Item(1)) FilePath = folder & "\" & file WScript.Echo folder WScript.echo file WScript.Echo FilePath ' Instantiate File System Object Set fso = CreateObject("Scripting.FileSystemObject") ' Instantiate MOM API Set oAPI = CreateObject("MOM.ScriptAPI") Set oBag = oAPI.CreatePropertyBag() ' Verify the path to the file exists xists If (fso.FolderExists(folder)) Then 'Folder exists, submit property bag and continue Call oBag.AddValue("FolderExists","Yes") WScript.Echo "Folder exists" Else 'Folder does not exist, submit property bag and exit Call oBag.AddValue("FolderExists","No") Call oBag.AddValue("FileExists","No") WScript.Echo "Folder doesn't exist" oAPI.AddItem(oBag) Call oAPI.ReturnItems Exit Sub End If ' Verify the file exists If (fso.FileExists(FilePath)) Then 'File exists, submit property bag and exit Call oBag.AddValue("FileExists","Yes") oAPI.AddItem(oBag) Call oAPI.ReturnItems Else 'File does not exist, submit property bag and exit Call oBag.AddValue("FileExists","No") WScript.Echo "File doesn't exist" oAPI.AddItem(oBag) Call oAPI.ReturnItems Exit Sub End If End Sub</ScriptBody> <TimeoutSeconds>30</TimeoutSeconds> <ErrorExpression> <Or> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="String">Property[@Name='FolderExists']</XPathQuery> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value Type="String">No</Value> </ValueExpression> </SimpleExpression> </Expression> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="String">Property[@Name='FileExists']</XPathQuery> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value Type="String">No</Value> </ValueExpression> </SimpleExpression> </Expression> </Or> </ErrorExpression> <SuccessExpression> <And> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="String">Property[@Name='FolderExists']</XPathQuery> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value Type="String">Yes</Value> </ValueExpression> </SimpleExpression> </Expression> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="String">Property[@Name='FileExists']</XPathQuery> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value Type="String">Yes</Value> </ValueExpression> </SimpleExpression> </Expression> </And> </SuccessExpression> </Configuration> </UnitMonitor> </Monitors> <Recoveries> <Recovery ID="Demo.FileExists.Monitor.CopyFile" Accessibility="Internal" Enabled="true" Target="Windows!Microsoft.Windows.Server.OperatingSystem" Monitor="Demo.FileExists.Monitor.FileExists" ResetMonitor="false" ExecuteOnState="Warning" Remotable="true" Timeout="300"> <Category>Custom</Category> <ConditionDetection ID="CD" TypeID="System!System.ExpressionFilter"> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="String">StateChange/DataItem/Context/DataItem/Property[@Name='FolderExists']</XPathQuery> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value Type="String">Yes</Value> </ValueExpression> </SimpleExpression> </Expression> </ConditionDetection> <WriteAction ID="SWA" TypeID="Windows!Microsoft.Windows.ScriptWriteAction"> <ScriptName>CopyFile.vbs</ScriptName> <Arguments /> <ScriptBody>Dim oAPI, fso Set oAPI = CreateObject("MOM.ScriptAPI") Call oAPI.LogScriptEvent("CopyFile.vbs",6002,0,"Starting copyfile script") set fso=CreateObject("Scripting.FileSystemObject") fso.CopyFile "\\scom01\AgentStuff\amd64\oomads.msi", "C:\Program Files\Microsoft Monitoring Agent\Agent\HelperObjects\" WScript.Quit </ScriptBody> <TimeoutSeconds>60</TimeoutSeconds> </WriteAction> </Recovery> </Recoveries> </Monitoring> <Presentation> <StringResources> <StringResource ID="Demo.FileExists.Monitor.FileExists_AlertMessageResourceID" /> </StringResources> </Presentation> <LanguagePacks> <LanguagePack ID="ENU" IsDefault="true"> <DisplayStrings> <DisplayString ElementID="Demo.FileExists.Monitor"> <Name>Demo File Exists Monitor MP</Name> </DisplayString> <DisplayString ElementID="Demo.FileExists.Monitor.CopyFile"> <Name>Copy File</Name> </DisplayString> <DisplayString ElementID="Demo.FileExists.Monitor.FileExists"> <Name>Demo File Exists Monitor</Name> <Description /> </DisplayString> <DisplayString ElementID="Demo.FileExists.Monitor.FileExists" SubElementID="Error"> <Name>Error</Name> </DisplayString> <DisplayString ElementID="Demo.FileExists.Monitor.FileExists" SubElementID="Success"> <Name>Success</Name> </DisplayString> <DisplayString ElementID="Demo.FileExists.Monitor.FileExists_AlertMessageResourceID"> <Name>Demo File Exists Monitor</Name> <Description>The expected file or folder is missing. FolderExists: {0} FileExists: {1}</Description> </DisplayString> </DisplayStrings> </LanguagePack> </LanguagePacks> </ManagementPack>
We will detect all the agents that are missing this file:
Give context:
Run a recovery:
And finally, redetect that the file exists, set the monitor to healthy, and close any associated alerts:
This is a VERY simple example… and this specific example should be modified as it will copy the AMD64 version of OOMADS.MSI to all agents missing it, even if they are 32bit. If you wanted to actually use this, I’d recommend changing the target to your domain controller class, and this assumes all your DC’s are 64bit (they sure should be!)
Regardless, this should be a fairly useful example for monitoring for the existence of a file, and how to pass a property to a condition detection in a recovery.
I will attach the demo MP below.
Brilliant stuff. So I am trying to do this but I don’t need a recovery task. My scenario is the oppisite, as in alert if the file exists.
I can get it to work when naming the exact file name (e.g. err.txt), but how do I make it work as a wildcard like err*.txt? The apps team need to know if any txt files prefixed with err turn up?
Thanks in advance!
Quite honestly – this is so old. I’d just write a powershell script to look for the existence of your files, and then put that in a timed script rule or monitor, using my Fragments I publish.
Hi Kevin,
I would have to monitor a file in a Unix server; path has been mentioned below. This file should be monitored and alert must be triggered when it gets modified or updated. Why I need this alert? If this file gets modified , server gets corrupted. Suggestions will be really helpful to me.
ls -l /var/spool/cron/erdiadm