Dealing with XML Events

With Windows Vista/2008 the "Crimson" event API's were added.  The idea was to allow events to be searchable/parse-able beyond treating the event description as a big block of text.

This was already addressed in a MOM Team blog post back in 2008: https://blogs.technet.com/b/momteam/archive/2008/02/01/authoring-event-rules-in-opsmgr.aspx  Here we are introduced to what SCOM calls event parameters, which are the Data nodes inside the EventData node of a Crimson event.

Another, more recent MOM Team blog post expanded on parsing the event XML with a namespace: https://blogs.technet.com/b/momteam/archive/2013/04/16/alerting-on-asp-net-exceptions-thru-the-windows-azure-management-pack.aspx

I recently had a challenge which required me to deeply dive into the UserData node of an event, which I tinkered with off-and-on before piecing together a solution.  I needed to monitor the CAPI2 logs for issues with expired or missing intermediate certificates on Windows 2008 R2.  The event description is not useful, and the event does not have an EventData node, so neither description parsing nor event parameters would help me.  There are other error event 30's in the same log that mean different things, so event ID and source alone were not adequate.  Here's a sample event:

Log Name: Microsoft-Windows-CAPI2/Operational
Source: Microsoft-Windows-CAPI2
Date: 3/26/2013 4:39:01 PM
Event ID: 30
Task Category: Verify Chain Policy
Level: Error
Keywords: Path Validation
User: SYSTEM
Computer: xxxx.xxxx.xxxx
Description:
For more details for this event, please refer to the "Details" section
Event Xml:
<Event xmlns="https://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Microsoft-Windows-CAPI2" Guid="{5bbca4a8-b209-48dc-a8c7-b23d3e5216fb}" />
    <EventID>30</EventID>
    <Version>0</Version>
    <Level>2</Level>
    <Task>30</Task>
    <Opcode>0</Opcode>
    <Keywords>0x4000000000000001</Keywords>
    <TimeCreated SystemTime="2013-03-26T16:39:01.654237800Z" />
    <EventRecordID>1696692</EventRecordID>
    <Correlation />
    <Execution ProcessID="520" ThreadID="11928" />
    <Channel>Microsoft-Windows-CAPI2/Operational</Channel>
    <Computer>xxxx.xxxx.xxxx</Computer>
    <Security UserID="S-1-5-18" />
  </System>
  <UserData>
    <CertVerifyCertificateChainPolicy>
      <Policy type="CERT_CHAIN_POLICY_NT_AUTH" constant="6" />
      <Certificate fileRef="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.cer" subjectName="Firstname Lastname" />
      <CertificateChain chainRef="{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" />
      <Flags value="40000000" BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_END_ENTITY_FLAG="true" />
      <Status chainIndex="0" elementIndex="1" />
      <EventAuxInfo ProcessName="lsass.exe" impersonateToken="S-1-5-18" />
      <CorrelationAuxInfo TaskId="{C286F78D-8E50-45EE-8A99-DC086979AD08}" SeqNumber="1" />
      <Result value="800B0112">A certification chain processed correctly, but one of the CA certificates is not trusted by the policy provider.</Result>
    </CertVerifyCertificateChainPolicy>
  </UserData>
</Event>

What I was asked to go after was the "type" attribute of the "Policy" node.  The tricky part is the XML Namespace.  When dealing with known/standard SCOM event log parameters you don't have to deal with it, but when doing a raw XPATH query, you do.  All of the [local-name()='xxxx'] bits are for getting around the XML namespace.  items that start with * indicate a node, and items that start with @* indicate an attribute.  Here is the XML and a screen shot of the event data source I used.

   <ComputerName>$Target/Host/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/NetworkName$</ComputerName>
  <LogName>Microsoft-Windows-CAPI2/Operational</LogName>
  <Expression>
    <And>
      <Expression>
        <SimpleExpression>
          <ValueExpression>
            <XPathQuery Type="UnsignedInteger">EventDisplayNumber</XPathQuery>
          </ValueExpression>
          <Operator>Equal</Operator>
          <ValueExpression>
            <Value Type="UnsignedInteger">30</Value>
          </ValueExpression>
        </SimpleExpression>
      </Expression>
      <Expression>
        <RegExExpression>
          <ValueExpression>
            <XPathQuery Type="String">PublisherName</XPathQuery>
          </ValueExpression>
          <Operator>ContainsSubstring</Operator>
          <Pattern>CAPI2</Pattern>
        </RegExExpression>
      </Expression>
      <Expression>
        <SimpleExpression>
          <ValueExpression>
            <XPathQuery Type="Integer">EventLevel</XPathQuery>
          </ValueExpression>
          <Operator>Equal</Operator>
          <ValueExpression>
            <Value Type="Integer">1</Value>
          </ValueExpression>
        </SimpleExpression>
      </Expression>
      <Expression>
        <SimpleExpression>
          <ValueExpression>
            <XPathQuery Type="String">EventData/DataItem/*[local-name()='UserData']/*[local-name()='CertVerifyCertificateChainPolicy']/*[local-name()='Policy']/@*[local-name()='type']</XPathQuery>
          </ValueExpression>
          <Operator>Equal</Operator>
          <ValueExpression>
            <Value Type="String">CERT_CHAIN_POLICY_NT_AUTH</Value>
          </ValueExpression>
        </SimpleExpression>
      </Expression>
    </And>
  </Expression>

  Here is how to put some of that data into the alert description:

  <Priority>1</Priority>
  <Severity>2</Severity>
  <AlertOwner></AlertOwner>
  <AlertMessageId>$MPElement[Name="Live.LSD.TerminalService.Monitoring.CAPI2.CERT_CHAIN_POLICY_NT_AUTH.Error.AlertMessage"]$</AlertMessageId>
  <AlertParameters>
    <AlertParameter1>$Target/Host/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</AlertParameter1>
    <AlertParameter2>$Data/EventDisplayNumber$</AlertParameter2>
    <AlertParameter3>$Data/EventSourceName$</AlertParameter3>
    <AlertParameter4>$Data/Channel$</AlertParameter4>
    <AlertParameter5>$Data/EventDescription$</AlertParameter5>
    <AlertParameter6>$Data/EventData/DataItem$</AlertParameter6>
    <AlertParameter7>$Data/EventData/DataItem/*[local-name()='UserData']/*[local-name()='CertVerifyCertificateChainPolicy']/*[local-name()='Policy']/@*[local-name()='type']$</AlertParameter7>
  </AlertParameters>
  <Suppression>
    <SuppressionValue>$Data/EventDisplayNumber$</SuppressionValue>
    <SuppressionValue>$Data/PublisherName$</SuppressionValue>
    <SuppressionValue>$Data/LoggingComputer$</SuppressionValue>
    <SuppressionValue>$Data/EventLevel$</SuppressionValue>
  </Suppression>
  <Custom1></Custom1>
  <Custom2></Custom2>
  <Custom3></Custom3>
  <Custom4></Custom4>
  <Custom5></Custom5>
  <Custom6></Custom6>
  <Custom7></Custom7>
  <Custom8></Custom8>
  <Custom9></Custom9>
  <Custom10></Custom10>

 $Data/EventData/DataItem$ appears to return the text of the XML.  Although I'm not sure if it would return all text, or just the text of the first or last node that has text.  In this particular example, only one node has text.  Everything else is attributes.