Wednesday, February 19, 2020

EVENTS scan powershell

Bonjour,

Ceci est une formation pour utiliser un script powershell qui scanne les événements, locaux ou distants, avec un filterxpath ou avec une recherche par chaîne.

Requis:
windows, server ou client
savoir le nom du log à scanner
windows à jour 2020-02
powershell ISE

Requis:
Pour lire un log à distance, le port suivant doit être ouvert dans le firewall windows:
Donc sur l'ordinateur client distant (dans le même réseau local), appliquer la commande powershell suivante:
Enable-NetFirewallRule -DisplayGroup 'Remote Event Log Management'


Comment trouver un log d'événements quand on ne sait pas le nom du log:
Pour trouver par nom un log, exemple: applocker (utilitaire qui bloque les application louches comme les crypto lockers)
Commande à faire dans powershell ISE:
Get-WinEvent -ListLog *exe* -force | select logname

Résultat:
LogName                                           
-------                                           
Microsoft-Windows-AppLocker/EXE and DLL           
Microsoft-Windows-AppLocker/Packaged app-Execution

PS C:\Users\serge.fournier\DÉVICOM INC\Prog Devicom - Document Prog\event_list_ps>

Le log voulu s'appelle donc:
Microsoft-Windows-AppLocker/EXE and DLL 

Au début du script, plusieurs object powershell ont été créé en tant que paramètres pour l'exécution du script:
# what events to check   0 = not check, 1 =check
$events_log_to_check01 = New-Object PsObject
$events_log_to_check01 | Add-Member NoteProperty -Name system -value '1'     # hardware
$events_log_to_check01 | Add-Member NoteProperty -Name security -value '0'   # logins
$events_log_to_check01 | Add-Member NoteProperty -Name updates -value '0'    # windows update
$events_log_to_check01 | Add-Member NoteProperty -Name dpmbackups -value '0' # backup azure microsoft
$events_log_to_check01 | Add-Member NoteProperty -Name applocker -value '0'  # bloqueur d'applications contre crypto virus

Par défaut, dans le script, plusieurs events log sont déjà prévu pour être scannés.
C'est-à-dire qu'il y a déjà des paramètres pour les scanner, mais ils sont à "off"
Pour qu'un log soit scanné, il faut que sa variable soit à 1.
Exemple:
$events_log_to_check01 | Add-Member NoteProperty -Name system -value '1'     # hardware

Le log system sera scanné

Par la suite une matrice (array) de paramètres est définie pour chaque log voulu:
Première matrice, les ordinateurs à scanner:
################################################################
# computers
################################################################
[string[]]$eventlogs_name_device_arr01 = "localhost"   # computer name to scan
#[string[]]$eventlogs_name_device_arr01+= "sql2008" # computer name to scan

Dans le rapport, chaque log sera un après l'autre, séparé par une ligne orange
Et chaque ordinateur scanné à la suite, à la fin du premier log
Ainsi, avec le bon id ou mot clef, on peut scanner les erreurs hardware d'un parc complet d'ordinateurs.

Deuxième matrice, les paramètres pour un log à scanner:
le nom du log
le provider (utilisé rarement)
le niveau d'erreur (en chiffre)
un mot à chercher qu'il faut mettre entre *. Exemple: *shut*, va trouver les shutdown
le nombre maximum d'événements
le nombre de jours à mettre dans le rapport
des filtres de ID à inclure (parfois on veut juste les login dans security)
des filtres de ID à NE PAS inclure (parfois il y a des id qui reviennent trop souvent pour rien (erreur com))


if($events_log_to_check01.system -eq 1)
{
    $n++
    [string[]]$eventslog_name_arr01[$n] = "system"
    [string[]]$eventslog_provider_arr01[$n] = "*"
    [string[]]$eventslog_level_arr01[$n] = "2"         # -1 = all 2 = ERROR, 3 = warning 4 = information
    [string[]]$eventslog_keywordtosearch01[$n] = "*"   # always use *word* (asterix at start and end)
    [string[]]$eventslog_maxevents_arr01[$n] = 100
    [string[]]$eventslog_daysback_arr01[$n] = 30       # -1 = no datetime filter

    [string[]]$eventslog_idtoget_arr01 = @()    # put other line in comment # for all events (0 count = no search by id)
    #[string[]]$eventslog_idtoget_arr01+= "107" # system wake from sleep
    #[string[]]$eventslog_idtoget_arr01+= "19"  # system update and description
    #[string[]]$eventslog_idtoget_arr01+= "22"  # system network loss
    #[string[]]$eventslog_idtoget_arr01+= "13"  # system shutdown
    $eventslog_idtoget_jarr01[$n] = $eventslog_idtoget_arr01

    $eventslog_idtoignore_arr01 = @()          # put other line in comment # for all events
    $eventslog_idtoignore_arr01+= "10016"      # com object error
    $eventslog_idtoignore_arr01+= "10028"      # com object error
    $eventslog_idtoignore_arr01+= "103"        # connectwise
    $eventslog_idtoignore_jarr01[$n] = $eventslog_idtoignore_arr01

    if($eventslog_daysback_arr01 -ne -1)
    {
        [DateTime[]]$eventslog_datetime_min_arr01[$n] = (Get-Date).AddDays(-$eventslog_daysback_arr01[$n])
        [DateTime[]]$eventslog_datetime_max_arr01[$n] = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    }
}

Résultat du scan:

event search by keyword mot recherché:

timecreatedMachineNamelognamelevelLeveldisplaynameuserididmessage
GoBackDays: 30MaxEvents: 100system2StringSearch: *IdNotWanted: 10016 10028 103IDWanted:system orange01
2020-02-19 09:56:23dev47.ad.devicom.comsystem2Erreur7000Le service IntelHaxm n'a pas pu démarrer en raison de l'erreur : Un périphérique attaché au système ne fonctionne pas correctement.

Script complet:

####################################################################################################################
$Title01 = "Event search with filterxpath (level, date) and string search multiple computers multiple event log"
####################################################################################################################
cls; write-host $title01 -ForegroundColor green
###############################
# requis
###############################
# security powershell execution
# windows 7  #   Set-ExecutionPolicy RemoteSigned
# windows 10 #   Set-ExecutionPolicy -scope currentuser RemoteSigned #   Set-ExecutionPolicy -scope localmachine RemoteSigned
################## find an event log by keyword
# Enable-NetFirewallRule -DisplayGroup 'Remote Event Log Management'
### search for an event log in all logs
# Get-WinEvent -ListLog *exe* -force | select logname

################### parameters
$method01 = 0   # 0 = use xfilterpath   1 = use string search in xml (outdated)

# what events to check   0 = not check, 1 =check
$events_log_to_check01 = New-Object PsObject
$events_log_to_check01 | Add-Member NoteProperty -Name system -value '1'     # hardware
$events_log_to_check01 | Add-Member NoteProperty -Name security -value '0'   # logins
$events_log_to_check01 | Add-Member NoteProperty -Name updates -value '0'    # windows update
$events_log_to_check01 | Add-Member NoteProperty -Name dpmbackups -value '0' # backup azure microsoft
$events_log_to_check01 | Add-Member NoteProperty -Name applocker -value '0'  # bloqueur d'applications contre crypto virus

# get the total of array we need to store events
[int]$log_tot = 0
foreach($object_properties in $events_log_to_check01.PsObject.Properties)
{
    #$object_properties.Name
    $log_tot += $object_properties.Value
}

$eventslog_name_arr01 = New-Object 'string[]' $log_tot
$eventslog_provider_arr01 = New-Object 'string[]' $log_tot
$eventslog_level_arr01 = New-Object 'string[]' $log_tot
$eventslog_keywordtosearch01 = New-Object 'string[]' $log_tot
$eventslog_maxevents_arr01 = New-Object 'string[]' $log_tot
$eventslog_daysback_arr01 = New-Object 'string[]' $log_tot
$eventslog_idtoget_jarr01 = New-Object 'object[]' $log_tot
$eventslog_idtoignore_jarr01 = New-Object 'Object[]' $log_tot
$eventslog_datetime_min_arr01 = New-Object 'string[]' $log_tot
$eventslog_datetime_max_arr01 = New-Object 'string[]' $log_tot

#Installation events can have an Event ID of 11707 or 1033. The InstallOperation field of these events indicate Installation completed.
#Uninstallation events can have an Event ID of 11724 or 1034. The InstallOperation field of these events indicate Removal completed.
#http://juventusitprofessional.blogspot.com/2015/07/windows-2012-dfs-dfs-r-part-5-list-of.html

################################################################
# computers
################################################################
[string[]]$eventlogs_name_device_arr01 = "localhost"   # computer name to scan
#[string[]]$eventlogs_name_device_arr01+= "sql2008" # computer name to scan
$n = -1

if($events_log_to_check01.system -eq 1)
{
    $n++
    [string[]]$eventslog_name_arr01[$n] = "system"
    [string[]]$eventslog_provider_arr01[$n] = "*"
    [string[]]$eventslog_level_arr01[$n] = "2"         # -1 = all 2 = ERROR, 3 = warning 4 = information
    [string[]]$eventslog_keywordtosearch01[$n] = "*"   # always use *word* (asterix at start and end)
    [string[]]$eventslog_maxevents_arr01[$n] = 100
    [string[]]$eventslog_daysback_arr01[$n] = 30       # -1 = no datetime filter

    [string[]]$eventslog_idtoget_arr01 = @()    # put other line in comment # for all events (0 count = no search by id)
    #[string[]]$eventslog_idtoget_arr01+= "107" # system wake from sleep
    #[string[]]$eventslog_idtoget_arr01+= "19"  # system update and description
    #[string[]]$eventslog_idtoget_arr01+= "22"  # system network loss
    #[string[]]$eventslog_idtoget_arr01+= "13"  # system shutdown
    $eventslog_idtoget_jarr01[$n] = $eventslog_idtoget_arr01

    $eventslog_idtoignore_arr01 = @()          # put other line in comment # for all events
    $eventslog_idtoignore_arr01+= "10016"      # com object error
    $eventslog_idtoignore_arr01+= "10028"      # com object error
    $eventslog_idtoignore_arr01+= "103"        # connectwise
    $eventslog_idtoignore_jarr01[$n] = $eventslog_idtoignore_arr01

    if($eventslog_daysback_arr01 -ne -1)
    {
        [DateTime[]]$eventslog_datetime_min_arr01[$n] = (Get-Date).AddDays(-$eventslog_daysback_arr01[$n])
        [DateTime[]]$eventslog_datetime_max_arr01[$n] = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    }
}
##############################
if($events_log_to_check01.security -eq 1)
{
   
    $n++
    [string[]]$eventslog_name_arr01[$n] = "security"
    [string[]]$eventslog_provider_arr01[$n] = "*"
    [string[]]$eventslog_level_arr01[$n] = "*"         # -1 = all 2 = ERROR, 3 = warning 4 = information
    [string[]]$eventslog_keywordtosearch01[$n] = "*serge*"   # always use *word* (asterix at start and end)
    [string[]]$eventslog_maxevents_arr01[$n] = 100
    [string[]]$eventslog_daysback_arr01[$n] = 30       # -1 = no datetime filter

    [string[]]$eventslog_idtoget_arr01 = @()    # put other line in comment # for all events (0 count = no search by id)
    #[string[]]$eventslog_idtoget_arr01+= "4624" # logon
    $eventslog_idtoget_jarr01[$n] = $eventslog_idtoget_arr01

    $eventslog_idtoignore_arr01 = @()          # put other line in comment # for all events
    #$eventslog_idtoignore_arr01+= "10016"      # com object error
    $eventslog_idtoignore_jarr01[$n] = $eventslog_idtoignore_arr01

    if($eventslog_daysback_arr01 -ne -1)
    {
        [DateTime[]]$eventslog_datetime_min_arr01[$n] = (Get-Date).AddDays(-$eventslog_daysback_arr01[$n])
        [DateTime[]]$eventslog_datetime_max_arr01[$n] = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    }
}
##############################
if($events_log_to_check01.updates -eq 1)
{
    #[string[]]$log_arr01+= "setup (installation, updates)"
    #[string[]]$eventid00 = "2 (update windows sans description, voir system pour celui avec description)"
   
    $n++
    [string[]]$eventslog_name_arr01[$n] = "setup (installation, updates)"
    [string[]]$eventslog_provider_arr01[$n] = "*"
    [string[]]$eventslog_level_arr01[$n] = "*"         # -1 = all 2 = ERROR, 3 = warning 4 = information
    [string[]]$eventslog_keywordtosearch01[$n] = "*"   # always use *word* (asterix at start and end)
    [string[]]$eventslog_maxevents_arr01[$n] = 25
    [string[]]$eventslog_daysback_arr01[$n] = 30       # -1 = no datetime filter

    [string[]]$eventslog_idtoget_arr01 = @()    # put other line in comment # for all events (0 count = no search by id)
    #[string[]]$eventslog_idtoget_arr01+= "4624" # logon
    $eventslog_idtoget_jarr01[$n] = $eventslog_idtoget_arr01

    $eventslog_idtoignore_arr01 = @()          # put other line in comment # for all events
    #$eventslog_idtoignore_arr01+= "10016"      # com object error
    $eventslog_idtoignore_jarr01[$n] = $eventslog_idtoignore_arr01

    if($eventslog_daysback_arr01 -ne -1)
    {
        [DateTime[]]$eventslog_datetime_min_arr01[$n] = (Get-Date).AddDays(-$eventslog_daysback_arr01[$n])
        [DateTime[]]$eventslog_datetime_max_arr01[$n] = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    }
}
###################################
if($events_log_to_check01.dpmbackups -eq 1)
{
    $n++
    [string[]]$eventslog_name_arr01[$n] = "dpm backup events"
    [string[]]$eventslog_provider_arr01[$n] = "*"
    [string[]]$eventslog_level_arr01[$n] = "3"         # -1 = all 2 = ERROR, 3 = warning 4 = information
    [string[]]$eventslog_keywordtosearch01[$n] = "*"   # always use *word* (asterix at start and end)
    [string[]]$eventslog_maxevents_arr01[$n] = 25
    [string[]]$eventslog_daysback_arr01[$n] = 14       # -1 = no datetime filter

    [string[]]$eventslog_idtoget_arr01 = @()    # put other line in comment # for all events (0 count = no search by id)
    #[string[]]$eventslog_idtoget_arr01+= "4624" # logon
    $eventslog_idtoget_jarr01[$n] = $eventslog_idtoget_arr01

    $eventslog_idtoignore_arr01 = @()          # put other line in comment # for all events
    #$eventslog_idtoignore_arr01+= "10016"      # com object error
    $eventslog_idtoignore_jarr01[$n] = $eventslog_idtoignore_arr01

    if($eventslog_daysback_arr01 -ne -1)
    {
        [DateTime[]]$eventslog_datetime_min_arr01[$n] = (Get-Date).AddDays(-$eventslog_daysback_arr01[$n])
        [DateTime[]]$eventslog_datetime_max_arr01[$n] = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    }
}

# reference ID for events
[string[]]$log_arr01 = "application (msi installer)"
[string[]]$eventid00 = "11707 (install completed)"
[string[]]$eventid00+= "1033 (install completed)"
[string[]]$eventid00+= "11724 (uninstall completed)"
[string[]]$eventid00+= "1034 (uninstall completed)"

############################################### display date
$date = get-date $eventslog_datetime_min_arr01[$n] -Format "yyyy-MM-dd HH:mm:ss"

########################################
# know events that are recurrents
########################################
# http://www.eventid.net/display-eventid-36887-source-Schannel-eventno-10676-phase-1.htm

########################################
# log file
########################################
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$scriptname = split-path -leaf $MyInvocation.MyCommand.Definition
$Logfilename = $scriptname + "_log.txt"
$logfile = $scriptPath + "\" + $Logfilename
$logall = 1

####################################
# color for thml report
####################################
[string[]]$error_message_arr = "WARNING"
[string[]]$error_color_arr = "yellow" # yellow
[string[]]$error_message_arr+= "avertissement"
[string[]]$error_color_arr+= "yellow" # yellow
[string[]]$error_message_Arr+= "ERROR"
[string[]]$error_color_arr+= "red" # red
[string[]]$error_message_Arr+= "ERReuR"
[string[]]$error_color_arr+= "red" # red
[string[]]$error_message_arr += "1900-01-01 00:00:00"
[string[]]$error_color_arr += "yellow" # yellow
[string[]]$error_message_arr += "00:00:00"
[string[]]$error_color_arr += "yellow" # yellow
[string[]]$error_message_arr += "inprogress"
[string[]]$error_color_arr += "yellow" # yellow
[string[]]$error_message_arr += "pending"
[string[]]$error_color_arr += "yellow" # yellow
[string[]]$error_message_Arr+= "failed"
[string[]]$error_color_arr+= "red" # red
[string[]]$error_message_Arr+= "true"
[string[]]$error_color_arr+= "red" # red
[string[]]$error_message_Arr+= "orange01"
[string[]]$error_color_arr+= "orange" # red

# if we search a keyword, the result cell will be orange if found
if($keywordtosearch01 -ne "*")
{
    #[string[]]$error_message_arr += "$keywordtosearch01"
    #[string[]]$error_color_arr += "orange" # yellow
}

#################################
# HTML formatting
#################################
$header = @"
<style>
BODY{background-color:white;}
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: LightBlue}
TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: white}
.green{background-color:#d5f2d5}
.blue{background-color:#e0eaf1}
.red{background-color:#ffd7de}
.yellow{background-color:#ffff00}
.orange{background-color:#ffa500}
</style>
"@

$msg01 = $title01
if ($logall=1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile}catch{}}

# loop computers

# agregation of all computers and logs for each computers
$events_jobs_arr01 = @()

foreach($eventlog_computer in $eventlogs_name_device_arr01)
{
    $msg01 = "Loop computers: " + $eventlog_computer
    write-host  -ForegroundColor Cyan
    if ($logall=1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}
   
    # loop logs (ex: system, application etc.)
    for($log_cnt =0; $log_cnt -lt $log_tot; $log_cnt++)
    {
        $msg01 = "Log: " + $eventslog_name_arr01[$log_cnt]
        if ($logall=1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}

        ##########################################
        # FILTERXPATH fabrication (faster filter)
        ##########################################
        #$eventid01 = @()
   
        #[DateTime]$date_time_max00 = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        #[DateTime]$date_time_min00 = (Get-Date).AddDays(-$gobackdays01)

        # using filterxpath to filter stuff is faster, the event are filtered BEFORE the result is sent to powershell
        # http://www.powershellish.com/blog/2014-12-09-get-winevent-filterxpath

        # filter datetime at source for filterxpath parameter
        $milliseconds01 = New-TimeSpan -Days $eventslog_daysback_arr01[$log_cnt] | Select-Object -ExpandProperty TotalMilliseconds

        #"*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) <= 86400000]] and EventData[Data[@Name='TargetUserName']='jdoe']]"
   
        $filterxpath01 = "*[System["
        $filterxpath01+= "TimeCreated[timediff(@SystemTime) <= $milliseconds01]"
   
        # level of error
        if($eventslog_level_arr01[$log_cnt] -ne -1 -and $eventslog_level_arr01[$log_cnt] -ne "*")
        {
            $level01 = $eventslog_level_arr01[$log_cnt]
            $filterxpath01+= " and Level=$level01"
        }

        # id wanted
        if($eventslog_idtoget_jarr01[$log_cnt].count -gt 1) { $filterxpath01+= " and (" }
        if($eventslog_idtoget_jarr01[$log_cnt].count -eq 1) { $filterxpath01+= " and" }
        for($idcnt=0;$idcnt -lt $eventslog_idtoget_jarr01[$log_cnt].count; $idcnt++)
        {
            $id01 = $eventslog_idtoget_jarr01[$log_cnt][$idcnt]
            if ($id01.length -gt 0)
            {
                # add OR at second element and more
                if($idcnt -gt 0) {$filterxpath01+= " or " }
                $filterxpath01+= " EventID=$id01"
            }
        }
        if($eventslog_idtoget_jarr01[$log_cnt].count -gt 1) { $filterxpath01+= ")" }

        # id NOT wanted
        for($idcnt=0;$idcnt -lt $eventslog_idtoignore_jarr01[$log_cnt].count; $idcnt++)
        {
            $id01 = $eventslog_idtoignore_jarr01[$log_cnt][$idcnt]
            if ($id01.length -gt 0)
            {
                $filterxpath01+= " and EventID!=$id01" # com error
            }
        }
       
        $filterxpath01+= "]" # close system related variable group
        #$filterxpath01+= " and EventData[Data[@Name='Utilisateur']='devicom']"
        #[EventData[Data='Sens' and Data='Logoff']]
        #$filterxpath01+= " and EventData[Data='windows']"
        $filterxpath01+= "]" # close main

        ###########################
        # main
        ###########################

        #[string]$date_time_min = $date_time_min00.tostring("yyyy-MM-dd HH:mm:ss")
        #[string]$date_time_max = $date_time_max00.tostring("yyyy-MM-dd HH:mm:ss")
        write-host "------------------------------------"
        write-host "Computer.........: " $eventlog_computer
        write-host "log..............: " $eventslog_name_arr01[$log_cnt]
        write-host "provider01.......: " $eventslog_provider_arr01[$log_cnt]
        write-host "level............: " $eventslog_level_arr01[$log_cnt]
        write-host "id to get........: " $eventslog_idtoget_jarr01[$log_cnt]
        write-host "id to ignore.....: " $eventslog_idtoignore_jarr01[$log_cnt]
        write-host "keywordtosearch01: " $eventslog_keywordtosearch01[$log_cnt]
        write-host "date_time_min....: " $eventslog_datetime_min_arr01[$log_cnt] #.tostring("yyyy-MM-dd HH:mm:ss")
        write-host "date_time_max....: " $eventslog_datetime_max_arr01[$log_cnt] #.tostring("yyyy-MM-dd HH:mm:ss")
        write-host "maxevents........: " $eventslog_maxevents_arr01[$log_cnt]

        #############################################
        # result psobject
        #############################################
        $events_job_obj01 = New-Object PsObject
        $events_job_obj01 | Add-Member NoteProperty -Name timecreated -value ''
        $events_job_obj01 | Add-Member NoteProperty -Name MachineName -value ''
        $events_job_obj01 | Add-Member NoteProperty -Name logname -value ''
        $events_job_obj01 | Add-Member NoteProperty -Name level -value ''
        $events_job_obj01 | Add-Member NoteProperty -Name Leveldisplayname -value ''
        $events_job_obj01 | Add-Member NoteProperty -Name userid -value ''
        $events_job_obj01 | Add-Member NoteProperty -Name id -value ''
        $events_job_obj01 | Add-Member NoteProperty -Name message -value ''

        ########################################
        # second header 1st line for each log
        ########################################
        $events_job_obj01.timecreated = "GoBackDays: " + $eventslog_daysback_arr01[$log_cnt]
        $events_job_obj01.MachineName = "MaxEvents: " + $eventslog_maxevents_arr01[$log_cnt]
        $events_job_obj01.logname = $eventslog_name_arr01[$log_cnt]
        $events_job_obj01.level = $eventslog_level_arr01[$log_cnt]
        $events_job_obj01.Leveldisplayname = "StringSearch: " + $eventslog_keywordtosearch01[$log_cnt]
        $events_job_obj01.userid = "IdNotWanted: " + $eventslog_idtoignore_jarr01[$log_cnt]
        $events_job_obj01.id = "IDWanted: " + $eventslog_idtoget_jarr01[$log_cnt]
        $events_job_obj01.message = $eventslog_name_arr01[$log_cnt] + " orange01"
        $events_jobs_arr01 += $events_job_obj01.PSObject.Copy()

        $usefilterxpath01 = 1
        if($usefilterxpath01 -eq 1)
        {
            [psobject]$global:events_report01 = $null
           
            if($eventslog_keywordtosearch01[$log_cnt] -ne "*")
            {
                # string search
                $command01 = {[psobject]$global:events_report01 = Get-WinEvent -ComputerName $eventlog_computer -LogName $eventslog_name_arr01[$log_cnt] `
                -MaxEvents $eventslog_maxevents_arr01[$log_cnt] -FilterXPath $FilterXPath01 -ErrorAction Stop | Where-Object {$_.Message -like $eventslog_keywordtosearch01[$log_cnt]}}
            }
            else
            {
                # no string search
                $command01 = {[psobject]$global:events_report01 = Get-WinEvent -ComputerName $eventlog_computer -LogName $eventslog_name_arr01[$log_cnt] `
                -MaxEvents $eventslog_maxevents_arr01[$log_cnt] -FilterXPath $FilterXPath01 -ErrorAction Stop}
            }
           
            write-host "Filterxpath...: $FilterXPath01" -ForegroundColor Yellow
            #write-host $command01 -ForegroundColor Yellow
            #$command02 = [scriptblock]::Create($command01)
           
            try
            {
                &$command01
                #[psobject]$events_report01 = Get-WinEvent -ComputerName $eventlog_computer -LogName $eventslog_name_arr01[$log_cnt] `
                #-MaxEvents $eventslog_maxevents_arr01[$log_cnt] -FilterXPath $FilterXPath01 -ErrorAction Stop
                $events_job_obj01 = $global:events_report01 | select Timecreated, MachineName, @{Name = 'Logname'; Expression = {$eventslog_name_arr01[$log_cnt]}} , level, Leveldisplayname,  userid, id, message
            }
            catch
            {
                write-host "error 0 results" -ForegroundColor Red
                $events_job_obj01.logname = $eventslog_name_arr01[$log_cnt]
               
                $msg01 = "ERROR getting events"
                $msg01+= $eventslog_name_arr01[$log_cnt]
                $msg01+= $_.exception.message

                $events_job_obj01.message = $msg01
                #write-host $_.ScriptStackTrace
                write-host $_.exception.message -ForegroundColor Red
            }
            #$global:events_report01
            #| format-table Timecreated, Leveldisplayname, message, id #-auto -wrap #| Out-String
        }
        else
        {
            # OLD search by TEXT, no xfilter
            [psobject]$events_report01 = Get-WinEvent -ComputerName $eventlog_computer -LogName $log01 -MaxEvents $maxevents01 `
            | Where-Object {`
            $_.providername -like $provider01 `
            -and $_.Message -like $keywordtosearch01 `
            -and $_.timecreated -gt $date_time_min `
            -and $_.level -eq 2 `
            -and $_.id -ne 10016 `
            -and $_.timecreated -le $date_time_max}
            #-and $_.Message -notlike "*Group Policy failed*" `
            #-and $_.level -eq 2 `
        }
        #$events_job_obj01 = $global:events_report01 | select Timecreated, MachineName, @{Name = 'Logname'; Expression = {$eventslog_name_arr01[$log_cnt]}} , level, Leveldisplayname,  userid, id, message

        $events_jobs_arr01 += $events_job_obj01.PSObject.Copy()
        #$events_jobs_arr01
    } # loop logs

} # loop computers

[xml]$event_xml01 = $events_jobs_arr01 | ConvertTo-Html -fragment

# xml for futur database
#$event_xml02 = ConvertTo-Xml -As "Document" -InputObject ($events_jobs_arr01  | select Timecreated, level, Leveldisplayname, MachineName, userid, message, id) #-Depth 3

# Parse XML object and set colour class according to value in last column ("Age")

$msg01 = "xml loop start"
if ($logall=1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}

for($trcnt=1;$trcnt -le $event_xml01.table.tr.count-1;$trcnt++)
{
    ################################################################
    # change color if certain keywords are found in html cell
    ################################################################
   
    for($tdcnt=1;$tdcnt -le $event_xml01.table.tr[$trcnt].ChildNodes.Count-1;$tdcnt++)
    {
        #write-host $tdcnt "   " $event_xml01.table.tr[$trcnt].td[$tdcnt]
        $item02 = $event_xml01.table.tr[$trcnt].td[$tdcnt]
        for($errorcnt = 0; $errorcnt -lt $error_message_arr.count; $errorcnt++)
        {
            $error_message = $error_message_arr[$errorcnt]
            if($item02 -like "*$error_message*")
            {
                $event_xml01.table.tr[$trcnt].ChildNodes[$tdcnt].SetAttribute('class',$error_color_arr[$errorcnt])
            }
        }
    }
}
$msg01 = "xml loop end"
if ($logall=1) {try{(Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $msg01 | Out-File $logfile -append}catch{}}

# Define body and append the modified XML object
$body = @"
<H2>event search by keyword mot recherché: $keywordtosearch01</H2>
$($event_xml01.innerxml)
"@

#####################
# html
#####################
$file03 = $scriptPath + "\event_list_array.htm"
# Convert to HTML and save the file
ConvertTo-Html -Head $header -Body $body | Out-File $file03

#########################
# xml
#########################
#$file04 = $scriptPath + "\event_list.xml"
#$event_xml02.save($file04)

if($events_jobs_arr01.count -gt 0)
{
    & $file03
}
Write-Host "Saved to:" $file03 -ForegroundColor Green