Public/Get-SCOMAlertKnowledge.ps1

Function Get-SCOMAlertKnowledge {
  <#
      .SYNOPSIS
      This function will get workflow details for one or more alerts, including the Knowledge Article content (neatly formatted in HTML).
 
      .DESCRIPTION
      Accepts one parameter, the alert object array of one or more SCOM alert objects: Microsoft.EnterpriseManagement.Monitoring.MonitoringAlert[]. This function can be useful with an automation/ticketing solution.
 
      .PARAMETER Alert
      The alert object(s) to be queried for the workflow details including the Knowledge article (if one exists).
 
      .PARAMETER MgmtServerFQDN
      Fully Qualified Domain Name of the SCOM management server.
      Alias: ManagementServer
 
      .PARAMETER OutputArticleOnly
      Will output only the Knowledge Article(s) content (neatly formatted in HTML). This may abe useful for situations where a service/ticketing connector is used to get alert properties.
 
      .EXAMPLE
      Get-SCOMAlert | Select-Object -First 1 | Get-SCOMAlertKnowledge
      Will display alert info (including Knowledge Article in HTML format) for the first alert object returned.
 
      .EXAMPLE
      PS C:\> Get-SCOMAlertKnowledge -Alert (Get-SCOMAlert | Select-Object -First 3) -OutputArticleOnly
      Will output only the HTML Knowledge Articles for the first 3 alert objects returned.
 
      .EXAMPLE
      PS C:\> Get-SCOMAlertKnowledge -Alert (Get-SCOMAlert -Name *sql*)
      Will output alert info (including Knowledge Article in HTML format) for any alerts with "sql" in the alert name.
 
      .NOTES
      Author: Tyson Paul
      Date: 2016/3/31
      Blog: https://monitoringguys.com/
  #>


  [CmdletBinding(DefaultParameterSetName='Parameter Set 1',
      SupportsShouldProcess=$true,
      SupportsPaging = $true,
  PositionalBinding=$true)]
  Param(

    [Parameter(Mandatory=$true,
        ValueFromPipeline=$true,
        ValueFromPipelineByPropertyName=$false,
        ValueFromRemainingArguments=$false,
        Position=0,
    ParameterSetName='Parameter Set 1')]
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Microsoft.EnterpriseManagement.Monitoring.MonitoringAlert[]]$Alert,

    [Parameter(Mandatory=$false,
        ValueFromPipeline=$false,
        ValueFromPipelineByPropertyName=$false,
        ValueFromRemainingArguments=$false,
        Position=1,
    ParameterSetName='Parameter Set 1')]
    [Alias("ManagementServer")]
    [string]$MgmtServerFQDN = "",

    [Parameter(Mandatory=$false,
        ValueFromPipeline=$false,
        ValueFromPipelineByPropertyName=$false,
        ValueFromRemainingArguments=$false,
        Position=2,
    ParameterSetName='Parameter Set 1')]
    [Switch]$OutputArticleOnly = $false

  )


  Begin {

    #------------------------------------------------------------------------------------
    Function ProcessArticle {
      Param(
        $article
      )
      If ($article -ne "None")    #some rules don't have any knowledge articles
      {
        #Retrieve and format article content
        $MamlText = $null
        $HtmlText = $null

        If ($null -ne $article.MamlContent)
        {
          $MamlText = $article.MamlContent
          $articleContent = fnMamlToHtml($MamlText)

        }

        If ($null -ne $article.HtmlContent)
        {
          $HtmlText = $article.HtmlContent
          $articleContent = fnTrimHTML($HtmlText)
        }
      }

      If ($null -eq $articleContent)
      {
        $articleContent = "No resolutions were found for this alert."
      }

      Return $articleContent
    }

    #------------------------------------------------------------------------------------
    Function ProcessWorkflow {
      Param(
        $Workflows,
        [string]$WFType
      )
      $myWFCollectionObj = @()

      ForEach ($thisWF in $Workflows) {
        $ErrorActionPreference = 'SilentlyContinue'
        $article = $thisWF.GetKnowledgeArticle($cultureInfo)
        If ($? -eq $false){
          $error.Remove($Error[0])
          $article = "None"
        }
        Else{
          $articleContent = ProcessArticle $article
        }

        $WFName = $thisWF.Name
        $WFDisplayName = $thisWF.DisplayName
        $WFDescription = $thisWF.Description
        $WFID = $thisWF.ID
        If ($WFDescription.Length -lt 1) {$WFDescription = "None"}

        #Note: Only a small subset of alert properties are gathered here. Add additional Note Properties as needed using the format below.
        $myWFObj = New-Object -TypeName System.Management.Automation.PSObject
        $myWFObj | Add-Member -MemberType NoteProperty -Name "WorkflowType" -Value $WFType
        $myWFObj | Add-Member -MemberType NoteProperty -Name "Name" -Value $WFName
        $myWFObj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $WFDisplayName
        $myWFObj | Add-Member -MemberType NoteProperty -Name "Description" -Value $WFDescription
        $myWFObj | Add-Member -MemberType NoteProperty -Name "ID" -Value $WFID
        $myWFObj | Add-Member -MemberType NoteProperty -Name "KnowledgeArticle" -Value $articleContent
        $myWFCollectionObj += $myWFObj
      }

      Return $myWFCollectionObj
    }

    #------------------------------------------------------------------------------------
    Function fnMamlToHTML{

      param
      (
        $MAMLText
      )
      $HTMLText = "";
      $HTMLText = $MAMLText -replace ('xmlns:maml="http://schemas.microsoft.com/maml/2004/10"');
      $HTMLText = $HTMLText -replace ("maml:para", "p");
      $HTMLText = $HTMLText -replace ("maml:");
      $HTMLText = $HTMLText -replace ("</section>");
      $HTMLText = $HTMLText -replace ("<section>");
      $HTMLText = $HTMLText -replace ("<section >");
      $HTMLText = $HTMLText -replace ("<title>", "<h3>");
      $HTMLText = $HTMLText -replace ("</title>", "</h3>");
      $HTMLText = $HTMLText -replace ("<listitem>", "<li>");
      $HTMLText = $HTMLText -replace ("</listitem>", "</li>");
      $HTMLText;
    }
    #------------------------------------------------------------------------------------
    Function fnTrimHTML($HTMLText){
      $TrimedText = "";
      $TrimedText = $HTMLText -replace ("&lt;", "<")
      $TrimedText = $TrimedText -replace ("&gt;", ">")
      <# $TrimedText = $TrimedText -replace ("<html>")
          $TrimedText = $TrimedText -replace ("<HTML>")
          $TrimedText = $TrimedText -replace ("</html>")
          $TrimedText = $TrimedText -replace ("</HTML>")
          $TrimedText = $TrimedText -replace ("<body>")
          $TrimedText = $TrimedText -replace ("<BODY>")
          $TrimedText = $TrimedText -replace ("</body>")
          $TrimedText = $TrimedText -replace ("</BODY>")
          $TrimedText = $TrimedText -replace ("<h1>", "<h3>")
          $TrimedText = $TrimedText -replace ("</h1>", "</h3>")
          $TrimedText = $TrimedText -replace ("<h2>", "<h3>")
          $TrimedText = $TrimedText -replace ("</h2>", "</h3>")
          $TrimedText = $TrimedText -replace ("<H1>", "<h3>")
          $TrimedText = $TrimedText -replace ("</H1>", "</h3>")
          $TrimedText = $TrimedText -replace ("<H2>", "<h3>")
          $TrimedText = $TrimedText -replace ("</H2>", "</h3>")
      #>

      $TrimedText;
    }
    #------------------------------------------------------------------------------------

    ###########################################################################################


    $ThisScript = $MyInvocation.MyCommand.Path
    $InstallDirectory =  (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft Operations Manager\3.0\Setup" -Name "InstallDirectory").InstallDirectory
    $PoshModulePath = Join-Path (Split-Path $InstallDirectory) PowerShell
    $env:PSModulePath += (";" + "$PoshModulePath")

    # Add 2012 Functionality/cmdlets
    . Import-SCOMPowerShellModule

    If ($Error) {
      $modulepaths=$env:PSModulePath.split(';')
      $Error.Clear()
    }

    # If no mgmt server name has been set, assume that local host is the mgmt server. It's worth a shot.
    If ($MgmtServerFQDN -eq "") {
      #Get FQDN of local host executing the script (could be any mgmt server when using SCOM2012 Resource Pools)
      $objIPProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
      If ($null -eq $objIPProperties.DomainName) {
        $MgmtServerFQDN = $objIPProperties.HostName
      }
      Else {
        $MgmtServerFQDN = $objIPProperties.HostName + "." +$objIPProperties.DomainName
      }
    }

    #Connect to Localhost Note: perhaps this can be done differently/cleaner/faster if connecting to self?
    New-SCManagementGroupConnection -Computer $MgmtServerFQDN | Out-Null
    # If ($Error) { LogIt -EventID 9995 -Type $warn -Message "Log any Errors..." ; $Error.Clear() }

    #Set Culture Info
    $cultureInfo = [System.Globalization.CultureInfo]'en-US'
    $objWFCollection = @()
  }

  Process{
    #Depending on how the alert object(s) are passed in, the ForEach may be needed. (parameter vs. piped)
    ForEach ($objAlert in $Alert)
    {
      $workflowID = $objAlert.MonitoringRuleId
      $bIsMonitorAlert = $objAlert.IsMonitorAlert
      If ($bIsMonitorAlert -eq $false) {
        $WFType = "Rule"
        $workflow = Get-SCOMRule -Id $workflowID
      }
      ElseIf ($bIsMonitorAlert -eq $true) {
        $WFType = "Monitor"
        $workflow = Get-SCOMMonitor -Id $workflowID
      }

      # The funciton being called is designed to accept one or more workflows.
      # It will return one or more custom objects with workfow details, including (most importantly) the KnowlegeArticle.
      $objWFCollection += ProcessWorkflow -Workflows $Workflow -WFType $WFType
    }
  } #End Process

  End{
    If ($OutputArticleOnly){
      Return $objWFCollection.KnowledgeArticle
    }
    Else{
      Return $objWFCollection
    }
  }
}