This week I received a nice question when working on a ACS project. The question was to get an alert when an interactive logon event occurs on a group of servers.
With this question the following requirements were asked:
- The alert should only be sent if the user account is member of an Active Directory group;
- The alert should only be generated for servers is which are member of a specific OU;
- The alert should be logged almost real-time.
Based on the above requirements I investigated the possible solutions and came with the following one:
- Alert collection is done by a event rule on the Security Log of the servers;
- The alert rule will only be enabled for a dynamic group which contains the servers based on their OU membership;
- A PowerShell script will close the alerts of users which are not member of the Active Directory group;
- A final notification will send the open alerts to end-users. (not part of this blogpost)
The above solution consists of a Management Pack which contains an event rule, group and an override. A PowerShell Script will be used to close the alert if it’s not from a member of the Active Directory group. Let’s start with the Management Pack. I will post below the important fragments. First the dynamic group, this group need to defined and needs a discovery to dynamically discover the servers of an OU. The Microsoft.SystemCenter.InstanceGroup.Library Management Pack must be added to the reference section of the Management Pack. The value of the ValueExpression need to be changed to the OU in your environment.
<TypeDefinitions> <EntityTypes> <ClassTypes> <ClassType ID="ACS.Security.Alerting.LogonEvent.Group" Accessibility="Public" Abstract="false" Base="MSIL!Microsoft.SystemCenter.InstanceGroup" Hosted="false" Singleton="true" Extension="false" /> </ClassTypes> </EntityTypes> </TypeDefinitions> <Monitoring> <Discoveries> <Discovery ID="ACS.Security.Alerting.LogonEvent.Group.DiscoveryRule" Enabled="true" Target="ACS.Security.Alerting.LogonEvent.Group" ConfirmDelivery="false" Remotable="true" Priority="Normal"> <Category>Discovery</Category> <DiscoveryTypes> <DiscoveryRelationship TypeID="MSIL!Microsoft.SystemCenter.InstanceGroupContainsEntities" /> </DiscoveryTypes> <DataSource ID="GroupPopulationDataSource" TypeID="SC!Microsoft.SystemCenter.GroupPopulator"> <RuleId>$MPElement$</RuleId> <GroupInstanceId>$MPElement[Name="ACS.Security.Alerting.LogonEvent.Group"]$</GroupInstanceId> <MembershipRules> <MembershipRule> <MonitoringClass>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</MonitoringClass> <RelationshipClass>$MPElement[Name="MSIL!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass> <Expression> <SimpleExpression> <ValueExpression> <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/OrganizationalUnit$</Property> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value>OU=Servers,DC=CONTOSO,DC=LOCAL</Value> </ValueExpression> </SimpleExpression> </Expression> </MembershipRule> </MembershipRules> </DataSource> </Discovery> </Discoveries>
Now when we have the dynamic group we can add the rule itself to the Management Pack. The rule is based on logon EventID 4624 (Win 2008 and later) and EventID 528 (Before Win 2008). Because I only want to see the interactive logons on a server I added another expression to the rule based on the logon type of the event (Params/Param[9]). Logon type 2 stands for an interactive logon and 10 for an remote interactive logon. The last expression was on the GUID of the logon. Every logon is logged twice in my eventlog. The first logged event has a nice GUID, the second event which was logged was equal to the first but had a GUID with only ‘0’ characters. So the last expression is the exclude the second event based on the GUID. See below the rule in XML:
<Rules> <Rule ID="ACS.Security.Alerting.LogonEvent.Rule" Enabled="false" Target="Windows!Microsoft.Windows.Computer" ConfirmDelivery="true" Remotable="true" Priority="Normal"> <Category>Alert</Category> <DataSources> <DataSource ID="DS" TypeID="Windows!Microsoft.Windows.EventProvider"> <ComputerName>$Target/Property[Type="Windows!Microsoft.Windows.Computer"]/NetworkName$</ComputerName> <LogName>Security</LogName> <Expression> <And> <Expression> <Or> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="String">Params/Param[9]</XPathQuery> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value Type="String">2</Value> </ValueExpression> </SimpleExpression> </Expression> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="String">Params/Param[9]</XPathQuery> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value Type="String">10</Value> </ValueExpression> </SimpleExpression> </Expression> </Or> </Expression> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="String">Params/Param[13]</XPathQuery> </ValueExpression> <Operator>NotEqual</Operator> <ValueExpression> <Value Type="String">{00000000-0000-0000-0000-000000000000}</Value> </ValueExpression> </SimpleExpression> </Expression> <Expression> <Or> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="UnsignedInteger">EventDisplayNumber</XPathQuery> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value Type="UnsignedInteger">4624</Value> </ValueExpression> </SimpleExpression> </Expression> <Expression> <SimpleExpression> <ValueExpression> <XPathQuery Type="UnsignedInteger">EventDisplayNumber</XPathQuery> </ValueExpression> <Operator>Equal</Operator> <ValueExpression> <Value Type="UnsignedInteger">528</Value> </ValueExpression> </SimpleExpression> </Expression> </Or> </Expression> </And> </Expression> </DataSource> </DataSources> <WriteActions> <WriteAction ID="Alert" TypeID="Health!System.Health.GenerateAlert"> <Priority>1</Priority> <Severity>0</Severity> <AlertOwner /> <AlertMessageId>$MPElement[Name="ACS.Security.Alerting.LogonEvent.Rule.AlertMessage"]$</AlertMessageId> <AlertParameters> <AlertParameter1>$Data/Params/Param[7]$</AlertParameter1> <AlertParameter2>$Data/Params/Param[6]$</AlertParameter2> <AlertParameter3>$Data/Params/Param[12]$</AlertParameter3> <AlertParameter4>$Data/@time$</AlertParameter4> </AlertParameters> <Suppression /> <Custom1>$Data/Params/Param[7]$</Custom1> <Custom2>$Data/Params/Param[6]$</Custom2> <Custom3>$Data/Params/Param[12]$</Custom3> <Custom4 /> <Custom5 /> <Custom6 /> <Custom7 /> <Custom8 /> <Custom9 /> <Custom10 /> </WriteAction> </WriteActions> </Rule> </Rules>
The final part of the management pack is the override which enables this rule for the dynamic group:
<Overrides> <RulePropertyOverride ID="ACS.Security.Alerting.LogonEvent.Rule.Override.Enablefor.ACS.Security.Alerting.LogonEvent.Group.DiscoveryRule" Context="ACS.Security.Alerting.LogonEvent.Group" Enforced="false" Rule="ACS.Security.Alerting.LogonEvent.Rule" Property="Enabled"> <Value>true</Value> </RulePropertyOverride> </Overrides> </Monitoring>
With the above management pack an alert is generated when a user logs on one of servers in a specific OU. The alert is generated almost realtime. With this MP we have met 2 requirements. The last requirement is that alerts only need to be send if the users are member of a Active Directory group. This last requirement is met through a PowerShell script which closes the alert if the user is not member of an Active Directory group. This PowerShell script uses the custom parameters which are set through the rule. The script:
#Get the variables [CmdletBinding()] Param( [Parameter(Mandatory=$True)] [string]$AlertId ) #Load ActiveDirectory CmdLets Import-Module ActiveDirectory #Load the SDK [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager.Common") | Out-Null [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager") | Out-Null #Connect to ManagementServer $MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings('<< SCOM MS SERVER >>') $ManagementGroup = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting) #Trim the {} $Alertid = $AlertId.Trim("{}") ##locate the specific alert $AlertSearchCriteria = "Id = `'$AlertID`'" $AlertCriteria = New-object Microsoft.EnterpriseManagement.Monitoring.MonitoringAlertCriteria($AlertSearchCriteria) $Alert = ($ManagementGroup.GetMonitoringAlerts($AlertCriteria))[0] $AlertUsername = $alert.CustomField2 $AlertStates = $ManagementGroup.GetMonitoringAlertResolutionStates() foreach($as in $AlertStates) { if($as.name -eq "Closed") { $ClosedAlertState = $as } } $groupmembers = Get-ADGroupMember -identity "Group" | Select-Object Name $UserNameCheck = $groupmembers.name -contains $AlertUsername if($UserNameCheck -eq $false) { $AlertMessage = "AlertID : "+$AlertId+" AlertUsername : "+$AlertUsername+" --> Alert closed by ACS Alert Security Script" $Alert.ResolutionState = $ClosedAlertState.ResolutionState $Alert.Update("Closed by ACS Security Alert Script") } else { $AlertMessage = "AlertID : "+$AlertId+" AlertUsername : "+$AlertUsername+" --> Alert not closed by ACS Alert Security Script" } #Debug Information to Log File #Add-Content D:\SOURCE\Output.txt -value $AlertMessage
This script need to be saved on every Management Server in your environment in the same file location. You need to install the Active Directory cmdlets on every SCOM Management Server which is member of your notification pool. Now we need to configure this script that it will run when a event is created inside SCOM. The following screens are the ‘detail’ screens from a Command Channel, Subscriber and a Subscription.
Command Channel details
Subscriber details:
Subscription details:
With the above management pack, PowerShell script and Notifcation Channel all requirements are met and logon events can be send through the existing notification channels.
If you want to receive a full copy of the Management Pack and the PowerShell file please send me a email and I will send you the files.