Batch create unit monitors?

Hi!
Is there any way to batch create unit monitors with powershell? I need to create mappings for vcenter alarms. So then I have the alert name for the alert in vcenter and then I need to create a monitor with that name.
I really don’t want to create 200 unit monitors by hand.

Example from a manually created vcenter alarm:

    <Monitors>
      <UnitMonitor ID="UIGeneratedMonitorxxxxxxxxxxxxxxxx" Accessibility="Public" Enabled="true" Target="Virt!Veeam.Virt.Extensions.VMware.vCenterEvents" ParentMonitorID="Health!System.Health.AvailabilityState" Remotable="true" Priority="Normal" TypeID="Virt1!Veeam.Virt.Extensions.VMware.EventLog.3SingleEventLog3StateUnitMonitorType.Extended" ConfirmDelivery="false">
        <Category>Custom</Category>
        <AlertSettings AlertMessage="UIGeneratedMonitorxxxxxxxxxxxxxxx_AlertMessageResourceID">
          <AlertOnState>Error</AlertOnState>
          <AutoResolve>true</AutoResolve>
          <AlertPriority>Normal</AlertPriority>
          <AlertSeverity>Error</AlertSeverity>
          <AlertParameters>
            <AlertParameter1>$Data[Default='']/Context/EventDescription$</AlertParameter1>
          </AlertParameters>
        </AlertSettings>
        <OperationalStates>
          <OperationalState ID="UIGeneratedOpStateIdxxxxxxxxxx" MonitorTypeStateID="AlarmStatustoGreen" HealthState="Success" />
          <OperationalState ID="UIGeneratedOpStateIdxxxxxxxxxxxxxxxx" MonitorTypeStateID="AlarmStatustoRed" HealthState="Error" />
          <OperationalState ID="UIGeneratedOpStateIdxxxxxxxxxxxxxx" MonitorTypeStateID="AlarmStatustoYellow" HealthState="Warning" />
        </OperationalStates>
        <Configuration>
          <AlarmName>Dell - System: Internal iDRAC Memory Unresponsive.</AlarmName>
          <TargetParamIndex>6</TargetParamIndex>
          <TypeProperty>$Target/Property[Type="SystemLibrary7585010!System.Entity"]/DisplayName$</TypeProperty>
        </Configuration>
      </UnitMonitor>
    </Monitors>

I’ll preface this by stating that I’m occasionally a bit mad.

However, I have recently started using DotLiquid to template out MPs in a similar way to what you’re describing.
There’s probably a much safer method using XML rendering, but this is super fast in my experience and you can build up a library of templates really quickly. The downside is that you get very little error-checking ahead of time.

So: “Here be dragons”

Start off with an exported MP. Rename this file how you like, just make sure the name starts with an underscore and ends with .liquid:

_basemp.liquid

We’re going to inherit from this file in a bit. This works (as far as I can tell) identically to Jinja inheritance if you’re familiar with Ansible or Python. In my example, I’m putting this in a subdirectory called templates.

This is pretty much a standard MP with the unit monitors snipped out and replaced with a block. Keeping this just to the monitoring subsection for brevity:

  <Monitoring>
    <Monitors>
      {% block monitors %}
      {% endblock %}
    </Monitors>
  </Monitoring>

Next set up a new file that we will use as the monitor template. We’ll use this to replace the block in the _basemp.liquid file.

{% extends "templates/basemp" %}

{% block monitors %}
{% for monitor in unitmonitors %}
<UnitMonitor ID="UIGeneratedMonitor{{ monitor.id }}" Accessibility="Public" Enabled="true" Target="Virt!Veeam.Virt.Extensions.VMware.vCenterEvents" ParentMonitorID="Health!System.Health.AvailabilityState" Remotable="true" Priority="Normal" TypeID="Virt1!Veeam.Virt.Extensions.VMware.EventLog.3SingleEventLog3StateUnitMonitorType.Extended" ConfirmDelivery="false">
<Category>Custom</Category>
<AlertSettings AlertMessage="UIGeneratedMonitor{{ monitor.id }}_AlertMessageResourceID">
    <AlertOnState>Error</AlertOnState>
    <AutoResolve>true</AutoResolve>
    <AlertPriority>Normal</AlertPriority>
    <AlertSeverity>Error</AlertSeverity>
    <AlertParameters>
    <AlertParameter1>$Data[Default='']/Context/EventDescription$</AlertParameter1>
    </AlertParameters>
</AlertSettings>
<OperationalStates>
    <OperationalState ID="UIGeneratedOpStateId{{ monitor.id }}green" MonitorTypeStateID="AlarmStatustoGreen" HealthState="Success" />
    <OperationalState ID="UIGeneratedOpStateId{{ monitor.id }}red" MonitorTypeStateID="AlarmStatustoRed" HealthState="Error" />
    <OperationalState ID="UIGeneratedOpStateId{{ monitor.id }}yellow" MonitorTypeStateID="AlarmStatustoYellow" HealthState="Warning" />
</OperationalStates>
<Configuration>
    <AlarmName>Dell - System: Internal iDRAC Memory Unresponsive.</AlarmName>
    <TargetParamIndex>6</TargetParamIndex>
    <TypeProperty>$Target/Property[Type="SystemLibrary7585010!System.Entity"]/DisplayName$</TypeProperty>
</Configuration>
</UnitMonitor>
{% endfor %}
{% endblock %}

I just called this monitors.liquid and dumped it in the same directory as the powershell script.
So loosely this expects an array of unit monitors with an id property each. You can give more properties if you like, I’m just being a bit lazy. We loop over each object in the array in what is effectively a foreach loop and then replace the variables.

That’s all our templating done, now onto the PowerShell Script:

# Get DotLiquid if not available
If (-not (Get-Package DotLiquid))
{
    Install-Package -Name DotLiquid -ProviderName NuGet -Scope CurrentUser -Force
}

# Load DotLiquid
$Package = Get-Item (Get-Package DotLiquid).source
[System.Reflection.Assembly]::LoadFrom(($Package.Directory.FullName + "\lib\netstandard2.0\DotLiquid.dll")) | Out-Null

# Configure DotLiquid to load templates based out of the current directory
[DotLiquid.Template]::FileSystem = [DotLiquid.FileSystems.LocalFileSystem]::new((Get-Location).path)

# Effectively a hashtable
$Variables = [DotLiquid.Hash]::new()

# Add an array of monitor ids, in this case 1-20
$Variables.Add('unitmonitors',@(
        1..20 | ForEach-Object {
            @{id = "$_"}
        }
    )
)

# Pull in our basic template from the file content
$TemplateContent = Get-Content .\monitors.liquid

# Parse this and turn it into a template
$Template = [DotLiquid.Template]::Parse($TemplateContent)

# Validate the XML and then save it to disk
$MPOutput = [xml]$Template.Render([DotLiquid.Hash]::FromDictionary($Variables))
$MPOutput.save('./MyTestMP.xml')

Hopefully, the comments there clear up what each step is doing.
And again there is probably a more sane method of doing what you want!

1 Like

Yes for sure… You are utterly quite mad :smiley:
I will take a look at your tip and see if it does the magic.
Cant understand why there is not any new-scomunitmonitor commandlet.

1 Like

Yeah, that would definitely be nice!
There is always the SCOM SDK but is significantly more complicated and it’s not something I’ve put enough time into learning. I’m still very much a novice programmer!
Hopefully, that will do the trick for you in lieu of that though. Let me know if you run into any trouble and I’ll try my best to help out! :grin:

Use snippets: How to use Snippets in VSAE to write LOTS of workflows, quickly! - Kevin Holman’s Blog

Use fragments: Authoring Management Packs – the fast and easy way, using Visual Studio??? - Kevin Holman’s Blog

Using fragments + Silect make it really easy. And Silect is building automation using Powershell for authoring. Silect Operations Portal - Silect Software

1 Like