switc.configmgr.LogReader.psm1

function read-SWITCCMLog {
    [CmdletBinding(
        DefaultParameterSetName="standard"
    )]
    param (
        [parameter(ParameterSetName="standard")]
        [parameter(ParameterSetName="export")]
            [string]$ContainsFilter,
        [parameter(ParameterSetName="standard")]
        [parameter(ParameterSetName="export")]
            [string]$ConfigMgrLogFile = "",
        [parameter(ParameterSetName="standard")]
        [parameter(ParameterSetName="export")]
            [string[]]$ErrorIndicators = @(
                "error",
                "failed",
                "failure",
                "fail",
                "exception"
            ),
        [parameter(ParameterSetName="standard")]
        [parameter(ParameterSetName="export")]
            [string[]]$WarningIndicators = @(
                "warning",
                "warn",
                "not to verify the signature of the scripts"
            ),
        [parameter(ParameterSetName="standard")]
        [parameter(ParameterSetName="export")]
            [string[]]$ErrorWarningExluceIndicators = @(
                "error code 0",
                "ContinueOnError=''",
                "Sending warning status message",
                "_SMSTSStatusSendFailed",
                "OSDDownloadContinueDownloadOnError",
                "ContinueOnError:"
            ),
        [parameter(ParameterSetName="export")][switch]$ExportAsJson
    )
    $ErrorActionPreference = "stop"

    enum MessageType {
        error
        warning
        info
    }

    $private:CharacterTranslationTable = @{
        "$([char]38)"='&'; # Char:&
        "$([char]34)"='"'; # Char:"
        "$([char]39)"='''; # Char:'
        "$([char]60)"='&lt;'; # Char:<
        "$([char]62)"='&gt;'; # Char:>
        #"$([char]59)"='&semi;'; # Char:;
        #"$([char]33)"='&excl;'; # Char:!
        #"$([char]35)"='&num;'; # Char:#
        #"$([char]36)"='&dollar;'; # Char:$
        #"$([char]37)"='&percnt;'; # Char:%
        #"$([char]40)"='&lpar;'; # Char:(
        #"$([char]41)"='&rpar;'; # Char:)
        #"$([char]42)"='&ast;'; # Char:*
        #"$([char]43)"='&plus;'; # Char:+
        #"$([char]44)"='&comma;'; # Char:,
        #"$([char]46)"='&period;'; # Char:.
        #"$([char]47)"='&sol;'; # Char:/
        #"$([char]92)"='&bsol;'; # Char:\
        #"$([char]58)"='&colon;'; # Char::
        #"$([char]61)"='&equals;'; # Char:=
        #"$([char]63)"='&quest;'; # Char:?
        #"$([char]91)"='&lsqb;'; # Char:[
        #"$([char]93)"='&rsqb;'; # Char:]
        #"$([char]123)"='&lcub;'; # Char:{
        #"$([char]125)"='&rcub;'; # Char:}
        #"$([char]124)"='&verbar;'; # Char:|
    }
    
    
    $XML = [Xml]::new()
    $Private:Content = Get-Content -path $ConfigMgrLogFile
    
    :fileLineLoop Foreach ($FileLineEntry in $Content) {
        <# old:
        $FileLineEntry = $FileLineEntry.Replace("![LOG[","MessageEntry>").Replace("]LOG]!><","</MessageEntry><MessageAttributes ")
        
        [int]$MessageStartIndex = $FileLineEntry.IndexOf('<MessageEntry>') + 14
        [int]$MessageEndIndex = $FileLineEntry.IndexOf('</MessageEntry>')
        #>

        
        If (-not ([string]::IsNullOrEmpty($FileLineEntry) -or ([string]::IsNullOrWhiteSpace($FileLineEntry)))) {
            If ($FileLineEntry.startsWith("Parsing",[System.StringComparison]::OrdinalIgnoreCase)) {
                $FileLineEntry = "<![LOG[" + $FileLineEntry
            }
            If ($FileLineEntry -ieq "<![LOG[") {
                continue fileLineLoop
            }
            [string]$FileLineEntry = "$($FileLineEntry.Substring(0,($FileLineEntry.Length -1)))/>"
            [int]$MessageStartIndex = 0
            [int]$MessageEndIndex = 0
            [int]$MessageAttributeStart = 0
            [int]$messageLengthIndex = 0
            If ($FileLineEntry.Contains("<![LOG[",[System.StringComparison]::OrdinalIgnoreCase)) {
                [int]$MessageStartIndex = $FileLineEntry.IndexOf('<![LOG[') + 7
                [int]$MessageEndIndex = $FileLineEntry.IndexOf(']LOG]!>')
                [int]$MessageAttributeStart = $FileLineEntry.IndexOf(']LOG]!><') + 8 
                [string]$MesssageAttributeEntry = "<MessageAttributes " + $FileLineEntry.Substring($MessageAttributeStart)
                If (($MessageEndIndex - $MessageStartIndex) -gt ($FileLineEntry.Length - $MessageStartIndex)) {
                    $messageLengthIndex = ($FileLineEntry.Length - $MessageStartIndex)
                }
                Else {
                    $messageLengthIndex = ($MessageEndIndex - $MessageStartIndex)
                }
                if (0 -gt $messageLengthIndex) {
                    $messageLengthIndex = 0
                }
                [string]$MessageEntry = $FileLineEntry.Substring($MessageStartIndex,$messageLengthIndex)
            }
            Else {
                $MessageEntry = $FileLineEntry
            }
            #[string]$MessageEntry = $MessageEntry.Replace(([char]59),$private:CharacterTranslationTable[([char]59)])
            [string]$MessageEntry = $MessageEntry.Replace(([char]38),$private:CharacterTranslationTable[([char]38)])
            foreach ($Character in $private:CharacterTranslationTable.Keys) {
                If (-not ($Character -in @( [char]38, [char]59 ))) {
                    $MessageEntry = $MessageEntry.Replace($Character,$private:CharacterTranslationTable[$Character])
                }
            }            
            #old: $FileLineEntry = "<LogEntry><MessageEntry>$($MessageEntry)</MessageEntry>$($FileLineEntry.Substring($FileLineEntry.IndexOf('<MessageAttributes')))</LogEntry>"
            $FileLineEntry = '<?xml version="1.0" encoding="UTF-8"?>' + "<LogEntry><MessageEntry>$($MessageEntry)</MessageEntry>$($MesssageAttributeEntry)</LogEntry>"
            Try {
                $XML.LoadXml($FileLineEntry)
            }
            catch {
                Try {
                    $FileLineEntry = '<?xml version="1.0" encoding="UTF-8"?>' + "<LogEntry><MessageEntry>ERROR CONVERTING MESSAGE!</MessageEntry>$($MesssageAttributeEntry)</LogEntry>"
                    $XML.LoadXml($FileLineEntry)
                }
                Catch {
                    continue fileLineLoop
                }
            }
            If (-not ([string]::IsNullOrEmpty($XML.LogEntry.MessageAttributes.Time))) {
                $XML.LogEntry.MessageAttributes.RemoveAttribute("Time")
                $XML.LogEntry.MessageAttributes.SetAttribute("Time","$($XML.LogEntry.MessageAttributes.Time.split('-')[0])")
                $private:TimeValue = $XML.LogEntry.MessageAttributes.Time.split('-')[0]
            }
            If (-not ([string]::IsNullOrEmpty($XML.LogEntry.MessageAttributes.date))) {
                $private:DateValue = $XML.LogEntry.MessageAttributes.date
            }        
            # $($XML.LogEntry.MessageAttributes.date) $()
            If (-not ([string]::IsNullOrEmpty($private:DateValue) -or ([string]::IsNullOrEmpty($private:TimeValue)))) {
                [DateTime]$private:MessageEntryDateTime = [DateTime]"$($private:DateValue) $($private:TimeValue)"
                $XML.LogEntry.MessageAttributes.RemoveAttribute("MessageLogged")
                $XML.LogEntry.MessageAttributes.SetAttribute("MessageLogged",[DateTime]$private:MessageEntryDateTime)
                $XML.LogEntry.MessageAttributes.RemoveAttribute("MessageLoggedFileTime")
                $XML.LogEntry.MessageAttributes.SetAttribute("MessageLoggedFileTime",[DateTime]$Private:MessageEntryDateTime.ToFileTime())
            }
    
            $XML.LogEntry.MessageAttributes.RemoveAttribute("MessageType")
            $XML.LogEntry.MessageAttributes.SetAttribute("MessageType","$([MessageType]::info)")
            # Warning includes
            Foreach ($WarningIndicator in $WarningIndicators) {
                If ($XML.LogEntry.MessageEntry.contains($WarningIndicator,[System.StringComparison]::OrdinalIgnoreCase)) {
                    $XML.LogEntry.MessageAttributes.RemoveAttribute("MessageType")
                    $XML.LogEntry.MessageAttributes.SetAttribute("MessageType","$([MessageType]::warning)")
                }
            }
            # Error includes
            Foreach ($ErrorIndicator in $ErrorIndicators) {
                If ($XML.LogEntry.MessageEntry.contains($ErrorIndicator,[System.StringComparison]::OrdinalIgnoreCase)) {
                    $XML.LogEntry.MessageAttributes.RemoveAttribute("MessageType")
                    $XML.LogEntry.MessageAttributes.SetAttribute("MessageType","$([MessageType]::error)")
                }
            }
            # exludes
            Foreach ($ExcludeIndicator in $ErrorWarningExluceIndicators) {
                If ($XML.LogEntry.MessageEntry.contains($ExcludeIndicator,[System.StringComparison]::OrdinalIgnoreCase)) {
                    $XML.LogEntry.MessageAttributes.RemoveAttribute("MessageType")
                    $XML.LogEntry.MessageAttributes.SetAttribute("MessageType","$([MessageType]::info)")
                }
            }  
            if (-not ($ExportAsJson)) {
                If (-not [string]::IsNullOrEmpty($ContainsFilter)) {
                    If (-not ($XML.LogEntry.MessageEntry.contains($ContainsFilter,[System.StringComparison]::OrdinalIgnoreCase))) {
                        continue fileLineLoop
                    }
                }
                Write-Host "$($XML.LogEntry.MessageAttributes.MessageLogged) " -ForegroundColor Gray -NoNewline
                Write-Host "| " -NoNewline
                switch ($XML.LogEntry.MessageAttributes.MessageType) {
                    "$([MessageType]::error)" {
                        Write-Host -Object "$($XML.LogEntry.MessageEntry)" `
                                   -ForegroundColor $GLOBAL:HOST.PrivateData.ErrorForegroundColor 
                    }
                    "$([MessageType]::warning)" {
                        Write-Host -Object "$($XML.LogEntry.MessageEntry)" `
                                   -ForegroundColor $GLOBAL:HOST.PrivateData.WarningForegroundColor 
                    }
                    "$([MessageType]::info)" {
                        Write-Host -Object "$($XML.LogEntry.MessageEntry)"
                    }
                }
            }
        }
    }   
}

Export-ModuleMember -Function "read-SWITCCMLog"