The Zurich release has arrived! Interested in new features and functionalities? Click here for more

Frank Tate
Giga Guru
Giga Guru

Creating and modifying a single Event Rule is very easy using the ServiceNow interface, but what if you want to see all of the Event Rule filters that you have in place? I've run across this problem enough times to realize that I needed a way to dump out all of this data is a format that I could easily read. And that's why I created a PowerShell script to perform this function. The following code does just that. It outputs an HTML document to stdout (so redirect it to a file to save it) that you can then view in a browser.

 Script:

[string]$userName = 'YOUR USERNAME'
[string]$userPassword = 'YOUR PASSWORD'

[securestring]$secStringPassword = ConvertTo-SecureString $userPassword -AsPlainText -Force
[pscredential]$credObject = New-Object System.Management.Automation.PSCredential ($userName, $secStringPassword)
$allrules=Invoke-WebRequest -Uri "https://YOURINSTANCE.service-now.com/api/now/table/em_match_rule" -Credential $credObject | ConvertFrom-Json

Write-Output "<html><body>"
foreach ($rule in $allrules.result) {
    Add-Member -InputObject $rule -NotePropertyName json_filter -NotePropertyvalue $($rule.simple_filter|ConvertFrom-Json);
    
    Write-Output ("<h1>" + $rule.name + "</h1>");
    Write-Output ("<code>Source = " + $rule.event_class);
    for ($i = 0; $i -lt $rule.json_filter.subpredicates[0].subpredicates.Count; $i++) {
        $topcombination=$rule.json_filter.subpredicates[0].compound_type.ToUpper();
        if ($i -eq 0) {
            $outstring = " AND</BR>(";
        } else {
            $outstring = "";
        }
        for ($j = 0; $j -lt $rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates.Count; $j++) {
            if ($j -eq 0) {
                $outstring = $outstring + "(";
            }
            $outstring = ($outstring + $rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates[$j].field.name + " " +
                $rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates[$j].operator.name + " " + 
                $rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates[$j].field.value)
            
            if ($j -lt ($rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates.Count - 1)) {
                $outstring = $outstring + " " + $rule.json_filter.subpredicates[0].subpredicates[$i].compound_type.ToUpper() + "</BR>";
            }
            
            if ($j -eq ($rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates.Count - 1)) {
                $outstring = ($outstring + ")");
            } 
        }
        if ($i -lt ($rule.json_filter.subpredicates[0].subpredicates.Count - 1)) {
            $outstring = ($outstring + " " + $topcombination + "</BR>");
        } else {
            $outstring = ($outstring + ")");
        }
        Write-Output $outstring;
        
        
    }
    Write-Output "</code>";
    
}
Write-Output "</body></html>";

 The following is a screenshot of the (somewhat ugly, but useful IMO) output produced:

find_real_file.png

Let me know if you have a usecase or suggestions for improvement!

Frank

Comments
Frank Tate
Giga Guru
Giga Guru

I thought about this a little more and came up with what may be a better solution. I created a new column named u_friendly_filter on the em_match_rule table. I also created a Business Rule to run before insert and update on that table. The body of that Business Rule is:

 

(function executeRule(current, previous /*null when async*/) {
    // Add your code here
    var parser = new JSONParser();
    var simplefilter = parser.parse(current.simple_filter);
    var event_class = current.event_class;
    var textstring = "Source = " + event_class;
    for (i = 0; i < simplefilter.subpredicates[0].subpredicates.length; i++) {
        var topcombination=simplefilter.subpredicates[0].compound_type.toUpperCase();
        if (i === 0) {
            textstring += " AND \n(";
        } else {
            textstring += "";
        }
        for (j = 0; j < simplefilter.subpredicates[0].subpredicates[i].subpredicates.length; j++) {
            if (j === 0) {
                textstring += "(";
            }
            textstring += simplefilter.subpredicates[0].subpredicates[i].subpredicates[j].field.name + " " +
            simplefilter.subpredicates[0].subpredicates[i].subpredicates[j].operator.name + " " + 
            simplefilter.subpredicates[0].subpredicates[i].subpredicates[j].field.value;
            
            if (j < simplefilter.subpredicates[0].subpredicates[i].subpredicates.length - 1) {
                textstring += " " + simplefilter.subpredicates[0].subpredicates[i].compound_type.toUpperCase() + "\n";
            }
            
            if (j == (simplefilter.subpredicates[0].subpredicates[i].subpredicates.length - 1)) {
                textstring += ")";
            } 
        }
        if (i < (simplefilter.subpredicates[0].subpredicates.length - 1)) {
            textstring += " " + topcombination + "\n";
        } else {
            textstring += ")";
        }
        // textstring is filled in at this point.
    }
    gs.addInfoMessage(textstring);
    current.u_friendly_filter = textstring;
})(current, previous);

 With this in place, the u_friendly_filter is updated to the human-readable string any time a new Event Rule is created or if an existing one is modified.

Frank

Frank Tate
Giga Guru
Giga Guru

I figured out most of the rest of the data and now have a more advanced version of the PowerShell script. Here's the script:

#$allrules=Get-Content -Raw .\AllEventRules.json | ConvertFrom-Json
# Define clear text string for username and password
[string]$userName = 'admin'
[string]$userPassword = 'YOURPASSWORD'

# Convert to SecureString
[securestring]$secStringPassword = ConvertTo-SecureString $userPassword -AsPlainText -Force
[pscredential]$credObject = New-Object System.Management.Automation.PSCredential ($userName, $secStringPassword)
$allrules=Invoke-WebRequest -Uri "https://YOURINSTANCE.service-now.com/api/now/table/em_match_rule" -Credential $credObject | ConvertFrom-Json

$allComposeFields=Invoke-WebRequest -Uri "https://YOURINSTANCE.service-now.com/api/now/table/em_compose_field" -Credential $credObject | ConvertFrom-Json

# table holding alert management rules: em_alert_management_rule
# column: alert_filter

$fullString = "<html><style>
table {
    width: 100%;
    border: 1px solid #000;
  }
  td {
    border: 1px solid #000;
}
  th.left {
    width: 15%
  }
  th.middle {
    width: 85%; 
  }
  h1 {
    background-color:lightblue;
  }
  </style>
  <body>
    <table>
        <thead>
            <tr>
                <th class='left'></th>
                <th class='middle'></th>
            </tr>
    </thead>
    ";
$oneFilterString = "";
$oneTransformString = "";
foreach ($rule in $allrules.result) {
    Add-Member -InputObject $rule -NotePropertyName json_filter -NotePropertyvalue $($rule.simple_filter|ConvertFrom-Json);
    Add-Member -InputObject $rule -NotePropertyName json_event_data -NotePropertyvalue $($rule.event_data|ConvertFrom-Json);
    
    $oneFilterString = ("<tr><td colspan=2><h1>" + $rule.name + "</h1></td></tr>");
    $oneFilterString =  ($oneFilterString + "<tr>
    <td><h2>Filter:</h2></td>
    <td><code>Source = " + $rule.event_class);
    for ($i = 0; $i -lt $rule.json_filter.subpredicates[0].subpredicates.Count; $i++) {
        $topcombination=$rule.json_filter.subpredicates[0].compound_type.ToUpper();
        if ($i -eq 0) {
            $oneFilterString = ($oneFilterString + " AND</BR>(");
        } else {
            $oneFilterString = ($oneFilterString + "");
        }
        for ($j = 0; $j -lt $rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates.Count; $j++) {
            if ($j -eq 0) {
                $oneFilterString = $oneFilterString + "(";
            }
            $oneFilterString = ($oneFilterString + $rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates[$j].field.name + " " +
                $rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates[$j].operator.name + " " + 
                $rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates[$j].field.value)
           
            if ($j -lt ($rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates.Count - 1)) {
                $oneFilterString = $oneFilterString + " " + $rule.json_filter.subpredicates[0].subpredicates[$i].compound_type.ToUpper() + "</BR>";
            }
            
            if ($j -eq ($rule.json_filter.subpredicates[0].subpredicates[$i].subpredicates.Count - 1)) {
                $oneFilterString = ($oneFilterString + ")");
            } 
        }
        if ($i -lt ($rule.json_filter.subpredicates[0].subpredicates.Count - 1)) {
            $oneFilterString = ($oneFilterString + " " + $topcombination + "</BR>");
        } else {
            $oneFilterString = ($oneFilterString + ")");
            $fullString = ($fullString + $oneFilterString)
        }
        
        
    }
    if ($rule.json_filter.subpredicates[0].subpredicates.Count -eq 0) {
        # this is the case where the filter is just Source = some_string
        $fullstring = ($fullstring + $oneFilterString)
    }
    $fullString = ($fullString + "</code></td></tr>");

    # now build the HTML to display the Transform and Compose Alert Output tab information
    # fun fact: the composed field expressions are stored in the em_compose_field table
    # where the 
    # "match_rule" field is a reference to the sys_id of the associated Event Rule
    # "field" is the name of the field in the Alert that we're composing and
    # "composition" is the composition of the field (e.g. "${resource}:${secondpart}")
    #
    #  Wow. So now I see why Event Rules with node = <blank> don't get processed correctly in update sets:
    #  for a rule, if you set field = ${field}  (same field name on left and right), then there's no goddamned
    #  entry in the em_compose_field table for that rule (!). Really. So I guess the logic I need to
    #  employ is:
    #
    #  for an Event Rule, get all compose fields and use those mappings.
    #  for any remaining fields, set field = ${field} in my output
    #

    $oneTransformString = "";
    
    for ($i = 0; $i -lt $rule.json_event_data.additionalInfoFields.Count; $i++) {
        # These are the new fields created by parsing existing fields
        if ($rule.json_event_data.additionalInfoFields[$i].regex -ne "") {
            if ($oneTransformString -eq "") {
                $oneTransformString = "<tr>
                <td colspan=2><h2>Transform info</h2></td>
            </tr>";
            }
            # This means that the field is being parsed to create other fields
            # that list of new fields is in mapping[x].fieldToMap.name
            $oneTransformString = ($oneTransformString + "<tr><td><B>Parsed field: </B></td><td>" + $rule.json_event_data.additionalInfoFields[$i].name + "</B></td></tr>");
            $oneTransformString = ($oneTransformString + "<tr><td><B>Regex: </B></td><td>" + $rule.json_event_data.additionalInfoFields[$i].regex + "</td></tr>");
            $oneTransformString = ($oneTransformString + "<tr><td><B>Mapped fields: </B></td><td>");
            for ($j = 0; $j -lt $rule.json_event_data.additionalInfoFields[$i].mapping.Count; $j++) {
                $oneTransformString = ($oneTransformString + $rule.json_event_data.additionalInfoFields[$i].mapping[$j].fieldToMap.name + "</BR>");
            }
            $oneTransformString = ($oneTransformString + "</td></tr>");
        }
    }


    for ($i = 0; $i -lt $rule.json_event_data.rawFields.Count; $i++) {
        # These are the new fields created by parsing existing fields
        if ($rule.json_event_data.rawFields[$i].regex -ne "") {
            if ($oneTransformString -eq "") {
                $oneTransformString = "<tr>
                <td colspan=2><h2>Transform info</h2></td>
                </tr>";
            }
            # This means that the field is being parsed to create other fields
            # that list of new fields is in mapping[x].fieldToMap.name
            $oneTransformString = ($oneTransformString + "<tr><td><B>Parsed field: </B></td><td>" + $rule.json_event_data.rawFields[$i].name + "</B></td></tr>");
            $oneTransformString = ($oneTransformString + "<tr><td><B>Regex: </B></td><td>" + $rule.json_event_data.rawFields[$i].regex + "</td></tr>");
            $oneTransformString = ($oneTransformString + "<tr><td><B>Mapped fields: </B></td><td>");
            for ($j = 0; $j -lt $rule.json_event_data.rawFields[$i].mapping.Count; $j++) {
                $oneTransformString = ($oneTransformString + $rule.json_event_data.rawFields[$i].mapping[$j].fieldToMap.name + "</BR>");
            }
            $oneTransformString = ($oneTransformString + "</td></tr>");
        }
    }
    
    $fullString = ($fullString + $oneTransformString);

    $oneRuleFieldsString = "<tr><td colspan=2><h2>Custom Compose Fields</h2></td></tr>";
    $thisRuleFields = $allComposeFields.result.where{$_.match_rule.value -eq $rule.sys_id};
    foreach ($field in $thisRuleFields) {
        $oneRuleFieldsString = ($oneRuleFieldsString + "<tr><td><B>" + $field.field + "</B> : </td><td>" + $field.composition + "</td></tr>");
    }
    if ($oneRuleFieldsString -eq "<tr><td colspan=2><h2>Custom Compose Fields</h2></td></tr>") {
        # do not update $fullString
    } else {
        $fullString = ($fullString + $oneRuleFieldsString);
    }

}
$fullString = ($fullString + "</table></body></html>");
$fullString > file.html;



 

And here's a snippet of the output:

Node response time dropped

Filter:

Source = SolarWinds AND
((description MATCH_RGX Node (.+) has dropped its average response time from above (.+)ms to (.+) which falls below the (.+)ms threshold\.))

Transform info

Parsed field: description
Regex: Node (.+) has dropped its average response time from above (.+)ms to (.+) which falls below the (.+)ms threshold\.
Mapped fields: hostNameFromEvent
avgResponseTime
newAvgresponseTime
responseTimeThreshdold

Custom Compose Fields

message_key : ${networkNodeId}_${netObjectId}_response_time

Nagios - IIS Short Name

Filter:

Source = Nagios AND
((type = IIS Web Server AND
node NOT LIKE .))

Custom Compose Fields

name : Microsoft IIS Server@${node}

AWS LB binding

Filter:

Source = AWS CloudWatch AND
((LoadBalancerName ISNOTEMPTY ))

Transform info

Parsed field: event_class
Regex: (.*):sns:(.*):(.*):(.*)
Mapped fields: prefix
region_id
punctuation1
punctuation2

Custom Compose Fields

object_id : ${LoadBalancerName}
node :

 

Frank

Version history
Last update:
‎06-05-2021 03:40 AM
Updated by: