Hi. I’m creating a monitor where I want multi instance cookdown to work. I’m checking against azure monitor for alerts and matches the alert rule id I have in SCOM with what i get from alerts in AM. Now the only parameter apart from credentials I use in the ps script is subscription ID. I would assume that SCOM fired one script for each subscription, but it does it for all objects (azure monitor rules). I have attached the monitor, data source and PS. Powershell scripts grab all Azure Monitor rules in the particular subscription and checks/matches against the alerts.
<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TypeDefinitions>
<ModuleTypes>
<DataSourceModuleType ID="ehrnst.AzureMonitor.AlertState.monitor.Datasource" Accessibility="Public">
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="IntervalSeconds" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="TimeoutSeconds" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SyncTime" type="xsd:string"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IntervalSeconds" Selector="$Config/IntervalSeconds$" ParameterType="int"/>
<OverrideableParameter ID="TimeoutSeconds" Selector="$Config/TimeoutSeconds$" ParameterType="int"/>
<OverrideableParameter ID="SyncTime" Selector="$Config/SyncTime$" ParameterType="string"/>
</OverrideableParameters>
<ModuleImplementation>
<Composite>
<MemberModules>
<DataSource ID="Scheduler" TypeID="System!System.Scheduler">
<Scheduler>
<SimpleReccuringSchedule>
<Interval>$Config/IntervalSeconds$</Interval>
<SyncTime>$Config/SyncTime$</SyncTime>
</SimpleReccuringSchedule>
<ExcludeDates/>
</Scheduler>
</DataSource>
<ProbeAction ID="PS" TypeID="Windows!Microsoft.Windows.PowerShellPropertyBagTriggerOnlyProbe">
<ScriptName>Get-AzureMonitorAlerts.ps1</ScriptName>
<ScriptBody>$IncludeFileContent/Rules and monitors/Scripts/Get-AzureMonitorAlerts.ps1$</ScriptBody>
<SnapIns/>
<Parameters>
<Parameter>
<Name>SourceID</Name>
<Value>$MPElement$</Value>
</Parameter>
<Parameter>
<Name>ManagedEntityID</Name>
<Value>$Target/Id$</Value>
</Parameter>
<!--The secret and ID to partner center multi tenant app-->
<Parameter>
<Name>MultiTenantAppId</Name>
<Value>$RunAs[Name="ICSP!ehrnst.Azure.CSP.MultiTenantApp.RunAsProfile"]/UserName$</Value>
</Parameter>
<Parameter>
<Name>MultiTenantAppSecret</Name>
<Value>$RunAs[Name="ICSP!ehrnst.Azure.CSP.MultiTenantApp.RunAsProfile"]/Password$</Value>
</Parameter>
<!--The customer tenant-->
<Parameter>
<Name>CustomerTenantId</Name>
<Value>$Target/Host/Host/Host/Property[Type="ICSP!ehrnst.Azure.CSPTenant.Class"]/TenantID$</Value>
</Parameter>
<Parameter>
<Name>CustomerSubscriptionId</Name>
<Value>$Target/Host/Host/Property[Type="ICSP!ehrnst.Azure.CSPSubscription.Class"]/SubscriptionID$</Value>
</Parameter>
<!--RUNAS ACCOUNT PARAMETERS-->
<Parameter>
<Name>CSPUsername</Name>
<Value>$RunAs[Name="ICSP!ehrnst.Azure.CSP.Account.RunAsProfile"]/UserName$</Value>
</Parameter>
<Parameter>
<Name>CSPPassword</Name>
<Value>$RunAs[Name="ICSP!ehrnst.Azure.CSP.Account.RunAsProfile"]/Password$</Value>
</Parameter>
</Parameters>
<TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>
<StrictErrorHandling>true</StrictErrorHandling>
</ProbeAction>
</MemberModules>
<Composition>
<Node ID="PS">
<Node ID="Scheduler"/>
</Node>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.PropertyBagData</OutputType>
</DataSourceModuleType>
</ModuleTypes>
<MonitorTypes>
<UnitMonitorType ID="ehrnst.AzureMonitor.AlertState" Accessibility="Internal">
<MonitorTypeStates>
<MonitorTypeState ID="Good"/>
<MonitorTypeState ID="Bad"/>
</MonitorTypeStates>
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="IntervalSeconds" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SyncTime" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="TimeoutSeconds" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MultitenantAppId" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MultitenantAppSecret" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="CustomerTenantId" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="CustomerSubscriptionId" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="CSPUsername" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="CSPPassword" type="xsd:string"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IntervalSeconds" ParameterType="int" Selector="$Config/IntervalSeconds$"/>
<OverrideableParameter ID="SyncTime" ParameterType="string" Selector="$Config/SyncTime$"/>
<OverrideableParameter ID="TimeoutSeconds" ParameterType="int" Selector="$Config/TimeoutSeconds$"/>
</OverrideableParameters>
<MonitorImplementation>
<MemberModules>
<DataSource TypeID="ehrnst.AzureMonitor.AlertState.monitor.Datasource" ID="DS">
<IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds>
<TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>
<SyncTime>$Config/SyncTime$</SyncTime>
</DataSource>
<ConditionDetection ID="FilterGood" TypeID="System!System.ExpressionFilter">
<Expression>
<And>
<Expression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="Integer">Property[@Name='State']</XPathQuery>
</ValueExpression>
<Operator>Equal</Operator>
<ValueExpression>
<Value Type="Integer">1</Value>
</ValueExpression>
</SimpleExpression>
</Expression>
<Expression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="String">Property[@Name='Object']</XPathQuery>
</ValueExpression>
<Operator>Equal</Operator>
<ValueExpression>
<Value Type="String">$Target/Property[Type="ehrnst.AzureMonitor.Rule.Class"]/RuleId$</Value>
</ValueExpression>
</SimpleExpression>
</Expression>
</And>
</Expression>
</ConditionDetection>
<ConditionDetection ID="FilterBad" TypeID="System!System.ExpressionFilter">
<Expression>
<And>
<Expression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="Integer">Property[@Name='State']</XPathQuery>
</ValueExpression>
<Operator>Equal</Operator>
<ValueExpression>
<Value Type="Integer">2</Value>
</ValueExpression>
</SimpleExpression>
</Expression>
<Expression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="String">Property[@Name='Object']</XPathQuery>
</ValueExpression>
<Operator>Equal</Operator>
<ValueExpression>
<Value Type="String">$Target/Property[Type="ehrnst.AzureMonitor.Rule.Class"]/RuleId$</Value>
</ValueExpression>
</SimpleExpression>
</Expression>
</And>
</Expression>
</ConditionDetection>
</MemberModules>
<RegularDetections>
<RegularDetection MonitorTypeStateID="Good">
<Node ID="FilterGood">
<Node ID="DS"/>
</Node>
</RegularDetection>
<RegularDetection MonitorTypeStateID="Bad">
<Node ID="FilterBad">
<Node ID="DS"/>
</Node>
</RegularDetection>
</RegularDetections>
<OnDemandDetections>
<OnDemandDetection MonitorTypeStateID="Good">
<Node ID="FilterGood">
<Node ID="DS" />
</Node>
</OnDemandDetection>
<OnDemandDetection MonitorTypeStateID="Bad">
<Node ID="FilterBad">
<Node ID="DS" />
</Node>
</OnDemandDetection>
</OnDemandDetections>
</MonitorImplementation>
</UnitMonitorType>
</MonitorTypes>
</TypeDefinitions>
<Monitoring>
<Monitors>
<UnitMonitor ID="ehrnst.AzureMonitor.AlertState.Monitor" Enabled="true" Target="ehrnst.AzureMonitor.Rule.Class" TypeID="ehrnst.AzureMonitor.AlertState" Accessibility="Public" ParentMonitorID="Health!System.Health.AvailabilityState">
<Category>AvailabilityHealth</Category>
<AlertSettings AlertMessage="ehrnst.AzureMonitor.AlertState.Monitor.Alert.Message">
<AlertOnState>Warning</AlertOnState>
<AutoResolve>true</AutoResolve>
<AlertPriority>Normal</AlertPriority>
<AlertSeverity>MatchMonitorHealth</AlertSeverity>
<AlertParameters>
<AlertParameter1>$Target/Property[Type="ehrnst.AzureMonitor.Rule.Class"]/ResourceShortName$</AlertParameter1>
<AlertParameter2>$Data/Context/Property[@Name='SignalType']$</AlertParameter2>
<AlertParameter3>$Target/Property[Type="ehrnst.AzureMonitor.Rule.Class"]/AlertDescription$</AlertParameter3>
<AlertParameter4>$Target/Property[Type="ehrnst.AzureMonitor.Rule.Class"]/ResourceId$</AlertParameter4>
<AlertParameter5>$Data/Context/Property[@Name='Severity']$</AlertParameter5>
</AlertParameters>
</AlertSettings>
<OperationalStates>
<OperationalState ID="GoodCondition" HealthState="Success" MonitorTypeStateID="Good"/>
<OperationalState ID="BadCondition" HealthState="Warning" MonitorTypeStateID="Bad"/>
</OperationalStates>
<Configuration>
<IntervalSeconds>302</IntervalSeconds>
<SyncTime></SyncTime>
<TimeoutSeconds>100</TimeoutSeconds>
<MultitenantAppId>$RunAs[Name="ICSP!ehrnst.Azure.CSP.MultiTenantApp.RunAsProfile"]/UserName$</MultitenantAppId>
<MultitenantAppSecret>$RunAs[Name="ICSP!ehrnst.Azure.CSP.MultiTenantApp.RunAsProfile"]/Password$</MultitenantAppSecret>
<CustomerTenantId>$Target/Host/Host/Host/Property[Type="ICSP!ehrnst.Azure.CSPTenant.Class"]/TenantID$</CustomerTenantId>
<CustomerSubscriptionId>$Target/Host/Host/Property[Type="ICSP!ehrnst.Azure.CSPSubscription.Class"]/SubscriptionID$</CustomerSubscriptionId>
<CSPUsername>$RunAs[Name="ICSP!ehrnst.Azure.CSP.Account.RunAsProfile"]/UserName$</CSPUsername>
<CSPPassword>$RunAs[Name="ICSP!ehrnst.Azure.CSP.Account.RunAsProfile"]/Password$</CSPPassword>
</Configuration>
</UnitMonitor>
</Monitors>
</Monitoring>
<Presentation>
<StringResources>
<StringResource ID="ehrnst.AzureMonitor.AlertState.Monitor.Alert.Message" />
</StringResources>
</Presentation>
<LanguagePacks>
<LanguagePack ID="ENU" IsDefault="true">
<DisplayStrings>
<DisplayString ElementID="ehrnst.AzureMonitor.AlertState.Monitor">
<Name>Azure Monitor Alert rule status monitor</Name>
<Description>Checks azure monitor for current alerts and match with alert rules discovered. Uses endponint: https://docs.microsoft.com/en-us/rest/api/monitor/alertsmanagement/alerts/getall </Description>
</DisplayString>
<DisplayString ElementID="ehrnst.AzureMonitor.AlertState.Monitor" SubElementID="BadCondition">
<Name>Bad Condition</Name>
</DisplayString>
<DisplayString ElementID="ehrnst.AzureMonitor.AlertState.Monitor" SubElementID="GoodCondition">
<Name>Good Condition</Name>
</DisplayString>
<DisplayString ElementID="ehrnst.AzureMonitor.AlertState.Monitor.Alert.Message">
<Name>Azure monitor alert triggered against {0}</Name>
<Description>
An alert in Azure Monitor targeting {3} fired.
Please see alert description for more details:
{2}
</Description>
</DisplayString>
</DisplayStrings>
<KnowledgeArticles>
<KnowledgeArticle ElementID="ehrnst.AzureMonitor.AlertState.Monitor" Visible="true">
<MamlContent>
<maml:section xmlns:maml="http://schemas.microsoft.com/maml/2004/10">
<maml:title>Summary</maml:title>
<maml:para>
GEts all alerts within azure monitor and matches with the alert rules.
Check the alert description for what may cause the error and use the Azure portal for further investigation.
</maml:para>
</maml:section>
<maml:section xmlns:maml="http://schemas.microsoft.com/maml/2004/10">
<maml:title>External</maml:title>
<maml:para>
...
</maml:para>
</maml:section>
</MamlContent>
</KnowledgeArticle>
</KnowledgeArticles>
</LanguagePack>
</LanguagePacks>
</ManagementPackFragment>
#=================================================================================
# Checks the health of all resources
#=================================================================================
param ($SourceId, $ManagedEntityId, $CustomerTenantId, $MultiTenantAppId, $MultiTenantAppSecret, $CSPPassword, $CSPusername, $CustomerSubscriptionId )
#=================================================================================
# Constants section - modify stuff here:
# Gather script start time
$StartTime = Get-Date
$CustomerSubscriptionId = $CustomerSubscriptionId.ToLower()
# Assign script name variable for use in event logging
$ScriptName = "Get-AzureMonitorAlerts.ps1"
#=================================================================================
# Gather who the script is running as
$whoami = whoami
#Load the MOMScript API and discovery propertybag
$momapi = New-Object -comObject "Mom.ScriptAPI"
#Log script event that we are starting task
$momapi.LogScriptEvent($ScriptName, 5322, 0, "Checking Azure monitor alerts. Running as ($whoami) For customer, $CustomerTenantId using: $MultiTenantAppId, $CustomerSubscriptionId")
# Begin Main Script
#=================================================================================
function Get-AADAppUserToken {
[CmdletBinding()]
param (
# Parameter help description
[Parameter()]
[string]
$ClientId,
# Parameter help description
[Parameter()]
[string]
$ClientSecret,
# Parameter help description
[Parameter()]
[string]
$tenantId,
# Parameter help description
[Parameter()]
[string]
$username,
# Parameter help description
[Parameter(Mandatory)]
[String]
$password
)
$loginurl = "https://login.windows.net/$tenantId/oauth2/token"
$Body = @{
resource = "https://management.core.windows.net/";
grant_type = "password";
client_id = $ClientId;
username = $username;
password = $Password;
client_secret = $ClientSecret;
}
Return Invoke-RestMethod -Uri $loginurl -Method POST -Body $body
}
$ApiVersion = "2018-05-05"
$result = Get-AADAppUserToken -tenantId $CustomerTenantId -ClientID $MultitenantAppId -ClientSecret $MultiTenantAppSecret -username $CSPUsername -password $CSPPassword -Verbose
if ($result -eq $null) {
$momapi.LogScriptEvent($ScriptName, 5322, 1, "Error. Failed to authenticate - terminating script" )
break
}
$AuthKey = "Bearer " + ($result.access_token)
$authHeader = @{
'Content-Type' = 'application/json;charset=utf-8'
'Accept' = 'application/json;charset=utf-8'
'Authorization' = $AuthKey
}
# get all alert rules and add them to a hash.
$rules = @{}
try {
$metricUri = 'https://management.azure.com/subscriptions/' + $CustomerSubscriptionId + '/providers/Microsoft.insights/metricAlerts?api-version=2018-03-01'
$metrics = (Invoke-RestMethod -method GET -Uri $metricUri -Headers $authHeader).value
$classicUri = 'https://management.azure.com/subscriptions/' + $CustomerSubscriptionId + '/providers/Microsoft.insights/Alertrules?api-version=2016-03-01'
$classics = (Invoke-RestMethod -method GET -Uri $classicUri -Headers $authHeader).value
foreach ($metric in $metrics) {
$rules.Add("$($metric.id)", "$($metric.properties.scopes)")
}
foreach ($classic in $classics) {
$rules.Add("$($classic.id)", "$($classic.properties.condition.dataSource.resourceUri)")
}
}
catch {
write-error $_
}
#Get all alerts within subscription
$alertUri = 'https://management.azure.com/subscriptions/' + $CustomerSubscriptionId + '/providers/Microsoft.AlertsManagement/alerts?api-version=' + $ApiVersion
$Alerts = Invoke-RestMethod -method GET -Uri $alertUri -Headers $authHeader -Verbose
$count = $($alerts.value).Count
$momapi.LogScriptEvent($ScriptName, 5323, 0, "found $count alerts in sub: $CustomerSubscriptionId URI: $alertUri")
# iterate all rules and alerts to allow for cook down. I hate nested foreach
foreach ($key in $rules.keys) {
$pbag = $momapi.CreatePropertyBag()
foreach ($alert in $alerts.value) {
$alertRuleId = $alert.properties.essentials.alertRule
$monitorCondition = $alert.properties.essentials.monitorCondition
$severity = $alert.properties.essentials.severity
$signalType = $alert.properties.essentials.signalType
$alertState = $alert.properties.essentials.alertState
$targetResource = $alert.properties.essentials.targetResource
$startDateTime = $alert.properties.essentials.startDateTime
# Set the state
if ($monitorCondition -eq "Fired" -and $alertState -eq "New" -and $key -eq $alertRuleId) {
$state = "2" # set error
}
else {
$state = "1"
}
}
$pbag.AddValue("State", $state)
$pbag.AddValue("Instance", $key)
$pbag.AddValue("AvailabilityState", $monitorCondition)
$pbag.AddValue("Object", $key)
$pbag.AddValue("Severity", $severity)
$pbag.AddValue("SignalType", $signalType)
$pbag
}
#=================================================================================
#Log an event for script ending and total execution time.
$EndTime = Get-Date
$ScriptTime = ($EndTime - $StartTime).TotalSeconds
$momapi.LogScriptEvent($ScriptName, 5322, 0, "Finished azure monitor alerts check. Script runtime was ($ScriptTime) seconds.")