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.")