All posts by Jonathan Almquist

Passing Boolean values to Powershell modules

Often times we need to pass a Boolean value to a Powershell provider, and a perfect example is for the purpose of a “debug” flag.

In my experience, Powershell probe and write action modules always type cast the script input parameter as String, whether the configuration element is of type Boolean or String. This means the default value in your workflow could be true, false, 1, or 0, and it will always equal true if you are handling this input parameter as Boolean in the script.

Obviously, this will cause some unexpected behaviors, and most likely will be viewed as a bug in the management pack.

The way I work around this issue (at least until this is fixed in the product) is NOT typing the script parameter, and then converting the variable to Boolean just after the param block like this:

param($debugFlag)
$debugFlag = [System.Convert]::ToBoolean($debugFlag)

By handling a Boolean input parameter like this in the script, we can effectively use the Boolean type in module configuration; the default value in the monitoring workflow can be true or false, the value will be handled appropriately by the script, the operator will see the default value as it should appear in console, and the operator will be presented with a meaningful (True|False) selector in the override interface.

UPDATE (09/02/2015) – This problem applies to Boolean, Integer, and Double values!

After writing this post, I wanted to get down to the bottom of the problem, so I poked around the Windows Library and what I found made it clear.

Taking a look at the implementation of any Powershell probe module, you will notice the Parameters element type is NamedParametersType, and this is defined in the Microsoft.Windows.PowerShellSchema. Notice the name-value pair are of type ID and string, respectively. This is the reason any type of value will be cast as String in a Powershell module.

<SchemaType ID="Microsoft.Windows.PowerShellSchema" Accessibility="Public"> 
<xsd:complexType name="NamedParametersType">
<xsd:sequence>
<xsd:element name="Parameter" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Name" type="xsd:ID" />
<xsd:element name="Value" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="SnapInsType">
<xsd:sequence>
<xsd:element name="SnapIn" minOccurs="0" maxOccurs="unbounded" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="NonNullString">
<xsd:restriction base="xsd:string">
<xsd:minLength value="1" />
</xsd:restriction>
</xsd:simpleType>
</SchemaType>

It seems to me, the way to resolve this is to update the library with a choice for value type. This would allow the developer the option to type cast, and would remove the assumption that type casting is occurring when it is not. Since this schema exists in the Microsoft Windows Library, it would make the most sense for Microsoft to fix this.

Unable to add the domain to the subject

Quick break/fix post here, because I was unable to find the solution to this subject anywhere in the community blogs or in KB articles.

I ran into this error while attempting to perform a manual installation of the System Center 2012 SP1 Operations Manager agent on a Linux (Ubuntu 10.04) server.

Full context of this error is as follows:

jonathan@ubuntu-01:/etc/opt/microsoft/scx$ sudo dpkg -i /home/jonathan/scx-1.4.0-906.universald.1.x64.deb (Reading database ... 44245 files and directories currently installed.) Preparing to replace scx 1.4.0.906 (using .../scx-1.4.0-906.universald.1.x64.deb) ... * Shutting down Microsoft SCX CIM Server: [fail] invoke-rc.d: initscript scx-cimd, action "stop" failed. Unpacking replacement scx ... Setting up scx (1.4.0.906) ... Checking existence of /lib64/libssl.so.0.9.8k and /lib64/libcrypto.so.0.9.8k ... Checking existence of /lib64/libssl.so.0.9.8 and /lib64/libcrypto.so.0.9.8 ... Found /lib64/libssl.so.0.9.8 and /lib64/libcrypto.so.0.9.8 ... Generating certificate with hostname="ubuntu-01", domainname="scomskills.com." WARNING! Could not read 256 bytes of random data from /dev/random. Will revert to less secure /dev/urandom. See the security guide for how to regenerate certificates at a later time when more random data might be available. Error generating SSL certificate: 'Unable to add the domain to the subject.' dpkg: error processing scx (--install): subprocess installed post-installation script returned error exit status 3 Processing triggers for ureadahead ... Errors were encountered while processing: scx

 

Long story short, the problem was resolved by modifying the /etc/resolv.conf file. Specifically, removing the trailing “dot” at the end of scomskills.com..

Here is the full context after modifying that file, which resulted in a successful installation:

jonathan@ubuntu-01:/etc/opt/microsoft/scx/ssl$ sudo nano /etc/resolv.conf jonathan@ubuntu-01:/etc/opt/microsoft/scx/ssl$ sudo rm /etc/opt/microsoft/scx//ssl/scx-key.pem jonathan@ubuntu-01:/etc/opt/microsoft/scx/ssl$ sudo dpkg -i /home/jonathan/scx-1.4.0-906.universald.1.x64.deb (Reading database ... 44245 files and directories currently installed.) Preparing to replace scx 1.4.0.906 (using .../scx-1.4.0-906.universald.1.x64.deb) ... * Shutting down Microsoft SCX CIM Server: [fail] invoke-rc.d: initscript scx-cimd, action "stop" failed. Unpacking replacement scx ... Setting up scx (1.4.0.906) ... Checking existence of /lib64/libssl.so.0.9.8k and /lib64/libcrypto.so.0.9.8k ... Checking existence of /lib64/libssl.so.0.9.8 and /lib64/libcrypto.so.0.9.8 ... Found /lib64/libssl.so.0.9.8 and /lib64/libcrypto.so.0.9.8 ... Generating certificate with hostname="ubuntu-01", domainname="scomskills.com" WARNING! Could not read 256 bytes of random data from /dev/random. Will revert to less secure /dev/urandom. See the security guide for how to regenerate certificates at a later time when more random data might be available. * Starting Microsoft SCX CIM Server: [ OK ] Processing triggers for ureadahead ...

 

Notice that I first removed the scx-key.pem file that was generated by the failed install, and then ran the installer package again – don’t actually know if this was necessary, but I thought it might be best to clean it up. As you can see, the final result is a signing of the certificate and the SCX CIM Server started successfully.

A little more background to the problem (if your interested):

The sequence of events that (I believe) led to this situation was the fact that I initially had the Linux server directly connected to an internet accessible access point, and the Ubuntu box was also configured to receive its network settings via DHCP. For some reason, DHCP added an extra “dot” in the resolv.conf file domain lines, and this apparently was an invalid configuration in the certificate signing process, thankfully everything is fine now, and I have been able to use the P4R-Gaming services more often.

Hope this helps someone out there in a similar situation.

WFAnalyzer.exe has stopped working

Just a quick post here, for those management pack developers that have run into the problem of simulating a workflow using the Visual Studio Authoring Extensions.

I have been missing the Workflow Analyzer companion to the MP Simulator for QUITE SOME TIME. I’ve tried troubleshooting the problem on several occasions, probably spending more than 10-12 hours burning midnight oil over the past months researching, debugging, sifting through logs and many Stack Overflow pages regarding .NET exceptions.

I’ve uninstalled and installed again the VSAE, and probably Visual Studio as well at some point, to no avail – the catastrophic behavior of Workflow Analyzer was clearly the bane of my development work. “The system cannot find the file specified?” What a benign message that is, especially when there is no file specified in the exception!

Just fixed it, though – out of what seems to be sheer luck.

I uninstalled Microsoft Monitoring Agent from my development workstation and installed the System Center 2012 SP1 agent – low and behold, the Workflow Analyzer sprung to life!

So happy now that I can actually see runtime data!

 

Here are a couple other references that didn’t provide a solution for me, but this issue seems very eluding and they might work in your case.

http://blogs.msdn.com/b/tysonpaul/archive/2014/07/01/workflow-analyzer-unhandled-exception-quot-the-system-cannot-find-the-file-specified-quot.aspx

http://blogs.inframon.com/post/2012/04/04/Management-Pack-Simulation-The-good-the-bad-and-the-%E2%80%A6-workaround.aspx

http://stackoverflow.com/questions/27596215/c-sharp-code-wont-launch-programs-win32exception-was-unhandled

Coupling time offset to monitoring interval

The requirements gathering phase of the management pack development lifecycle is critically important to the success of the project. Something that may come out of this phase is receiving company health check scripts, and this is an excellent opportunity to incorporate familiar company knowledge into a new monitoring solution.

These scripts might be used to check for some condition that may have occurred in the past n minutes or hours – n is referred to as a time offset in this case. This article will briefly describe a simple concept to a best practice around implementing this type of script in a custom data source.

This concept can be broken down into the simplest term, where n and monitoring interval share configuration.

For example, a script executes the following SQL query:

SELECT COUNT(Column1) as [Count], Name 
FROM MyDatabase
WHERE Timestamp BETWEEN DATEADD(minute,-60,GETDATE()) AND GETDATE()
GROUP BY Name

The part I want to draw your attention to is the WHERE clause in the SQL query, because this is where time offset comes into the picture – it is how time offset is identified, and allows for the implementation of this coupling concept.

The query above would return records that have been written in the past 60 minutes from now. When the script is plugged into a data source, “now” is the monitoring interval, which is configured on the scheduler that triggers script execution.

So, we conclude that “now” is IntervalSeconds on the simple scheduler module.

Now that we know we can couple time offset with monitoring interval, we can easily use the same value for both by sharing the same configuration. In order to do this, two minor changes need to be made in any script you plan to incorporate using this concept:

1. Ensure time offset is in seconds.
2. Replace the time offset value with the IntervalSeconds configuration.

In this scenario, we cover points 1 and 2 above by updating the 1st and 2nd arguments in the DATEADD function like this:
 
WHERE Timestamp BETWEEN DATEADD(second,-$Config/IntervalSeconds$,GETDATE()) AND GETDATE()

Now compose the module as usual…
Why is using this concept a good practice?

Monitoring interval is a standard override parameter, and inevitably it will be overridden – maybe not on this particular monitor, and maybe not until you’re long gone. But don’t assume the customer is going to keep the default interval – ever.

By coupling script time offsets to monitoring intervals, a basic interval override will not cause monitor state skewing.

Passing Data in Composite Workflow

I thought a quick and fun blog subject would be to build a composite workflow, passing data between each module, and writing output to the event log.

Workflow sequence:

  • Module1 outputs a ServiceName property bag data item – in this case “dhcp”.
  • Module2 accepts that ServiceName input, queries the service, and outputs Status property bag data item.
  • Module3 accepts that Status input, and simply writes it to the event log.
  • Composite module ties them together and executes in sequence.
  • Rule1 starts with a scheduler and ends with a write action, to execute the composite workflow.

Anyway – this is strictly a learning tool, but I thought it might be helpful to anyone interested in practicing composite workflows.

Here’s the full code:

<?xml version="1.0" encoding="utf-8"?> 
<ManagementPack SchemaVersion="2.0" ContentReadable="true" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Manifest>
<Identity>
<ID>CompositeModuleTest</ID>
<Version>1.0.0.3</Version>
</Identity>
<Name>CompositeModuleTest</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>
</References>
</Manifest>
<TypeDefinitions>
<ModuleTypes>
<WriteActionModuleType ID="composite" Accessibility="Internal">
<Configuration></Configuration>
<ModuleImplementation>
<Composite>
<MemberModules>
<WriteAction ID="com1" TypeID="module1" />
<WriteAction ID="com2" TypeID="module2" />
<WriteAction ID="com3" TypeID="module3" />
</MemberModules>
<Composition>
<Node ID="com3">
<Node ID="com2">
<Node ID="com1" />
</Node>
</Node>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.PropertyBagData</OutputType>
<InputType>System!System.BaseData</InputType>
</WriteActionModuleType>
<WriteActionModuleType ID="module1" Accessibility="Internal">
<Configuration></Configuration>
<ModuleImplementation>
<Composite>
<MemberModules>
<WriteAction ID="script1" TypeID="Windows!Microsoft.Windows.PowerShellPropertyBagWriteAction">
<ScriptName>script1.ps1</ScriptName>
<ScriptBody>

$api= new-object -comObject "MOM.ScriptAPI"
$api.LogScriptEvent('script1.ps1',100,4,"start")
$bag = $api.CreatePropertyBag()
$bag.AddValue("ServiceName","dhcp")
$bag

</ScriptBody>
<Parameters></Parameters>
<TimeoutSeconds>300</TimeoutSeconds>
</WriteAction>
</MemberModules>
<Composition>
<Node ID="script1" />
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.PropertyBagData</OutputType>
<InputType>System!System.BaseData</InputType>
</WriteActionModuleType>
<WriteActionModuleType ID="module2" Accessibility="Internal">
<Configuration></Configuration>
<ModuleImplementation>
<Composite>
<MemberModules>
<WriteAction ID="script2" TypeID="Windows!Microsoft.Windows.PowerShellPropertyBagWriteAction">
<ScriptName>script2.ps1</ScriptName>
<ScriptBody>

param($ServiceName)
$api= new-object -comObject "MOM.ScriptAPI"
$api.LogScriptEvent('Script2.ps1',100,4,$ServiceName)
$service = get-service $ServiceName
$bag = $api.CreatePropertyBag()
$bag.AddValue("ServiceName",$serviceName)
$bag.AddValue("Status",$service.status.ToString())
$bag

</ScriptBody>
<Parameters>
<Parameter>
<Name>ServiceName</Name>
<Value>$Data/Property[@Name="ServiceName"]$</Value>
</Parameter>
</Parameters>
<TimeoutSeconds>300</TimeoutSeconds>
</WriteAction>
</MemberModules>
<Composition>
<Node ID="script2" />
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.PropertyBagData</OutputType>
<InputType>System!System.BaseData</InputType>
</WriteActionModuleType>
<WriteActionModuleType ID="module3" Accessibility="Internal">
<Configuration></Configuration>
<ModuleImplementation>
<Composite>
<MemberModules>
<WriteAction ID="script3" TypeID="Windows!Microsoft.Windows.PowerShellPropertyBagWriteAction">
<ScriptName>script3.ps1</ScriptName>
<ScriptBody>

param($Status)
$api= new-object -comObject "MOM.ScriptAPI"
$api.LogScriptEvent('Script3.ps1',100,4,$Status)

</ScriptBody>
<Parameters>
<Parameter>
<Name>Status</Name>
<Value>$Data/Property[@Name="Status"]$</Value>
</Parameter>
</Parameters>
<TimeoutSeconds>300</TimeoutSeconds>
</WriteAction>
</MemberModules>
<Composition>
<Node ID="script3" />
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.PropertyBagData</OutputType>
<InputType>System!System.BaseData</InputType>
</WriteActionModuleType>
</ModuleTypes>
</TypeDefinitions>
<Monitoring>
<Rules>
<Rule ID="rule1" Enabled="true" Target="Windows!Microsoft.Windows.Computer">
<Category>Operations</Category>
<DataSources>
<DataSource ID="schedule" TypeID="System!System.SimpleScheduler">
<IntervalSeconds>10</IntervalSeconds>
<SyncTime />
</DataSource>
</DataSources>
<WriteActions>
<WriteAction ID="WA1" TypeID="composite" />
</WriteActions>
</Rule>
</Rules>
</Monitoring>
</ManagementPack>