
- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 06-05-2021 03:40 AM
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:
Let me know if you have a usecase or suggestions for improvement!
Frank

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
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

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
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 |
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 |
Custom Compose Fields |
|
name : | Microsoft IIS Server@${node} |
AWS LB binding |
|
Filter: |
Source = AWS CloudWatch AND |
Transform info |
|
Parsed field: | event_class |
Regex: | (.*):sns:(.*):(.*):(.*) |
Mapped fields: | prefix region_id punctuation1 punctuation2 |
Custom Compose Fields |
|
object_id : | ${LoadBalancerName} |
node : |
Frank