PSWinReportingV2.psm1

function Find-EventsTo {
    [CmdletBinding()]
    param ([Array] $Events,
        [alias('IgnoreWords', 'PriorityWords')][System.Collections.IDictionary] $DataSet,
        [switch] $Ignore,
        [switch] $Prioritize)
    if ($DataSet.Count -eq 0) { return $Events }
    $EventsToReturn = foreach ($Event in $Events) {
        $Found = $false
        foreach ($Set in $DataSet.GetEnumerator()) {
            if ($Set.Value -ne '') {
                foreach ($Value in $Set.Value) {
                    $ColumnName = $Set.Name
                    if ($Event.$ColumnName -like $Value) { $Found = $true }
                }
            }
        }
        if ($Ignore) { if (-not $Found) { $Event } }
        if ($Prioritize) { if ($Found) { $Event } }
    }
    return $EventsToReturn
}
function Get-EventsOutput {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Definitions,
        [Array] $AllEvents,
        [switch] $Quiet)
    $Results = @{ }
    foreach ($Report in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled' }) {
        if ($Definitions.$Report.Enabled) {
            if (-not $Quiet) { $Logger.AddInfoRecord("Running $Report") }
            $TimeExecution = Start-TimeLog
            $Results.$Report = foreach ($SubReport in $Definitions.$Report.Keys | Where-Object { $_ -notcontains 'Enabled', 'SqlExport' }) {
                if ($Definitions.$Report.$SubReport.Enabled) {
                    if (-not $Quiet) { $Logger.AddInfoRecord("Running $Report with subsection $SubReport") }
                    [string] $EventsType = $Definitions.$Report.$SubReport.LogName
                    [Array] $EventsNeeded = $Definitions.$Report.$SubReport.Events
                    [Array] $EventsFound = Get-EventsTranslation -Events $AllEvents -EventsDefinition $Definitions.$Report.$SubReport -EventIDs $EventsNeeded -EventsType $EventsType
                    if (-not $Quiet) { $Logger.AddInfoRecord("Ending $Report with subsection $SubReport events found $($EventsFound.Count)") }
                    $EventsFound
                }
            }
            $ElapsedTimeReport = Stop-TimeLog -Time $TimeExecution -Option OneLiner
            if (-not $Quiet) { $Logger.AddInfoRecord("Ending $Report - Time to run $ElapsedTimeReport") }
        }
    }
    return $Results
}
function Get-EventsTranslation {
    [CmdletBinding()]
    param([Array] $Events,
        [System.Collections.IDictionary] $EventsDefinition,
        [Array] $EventIDs,
        [string] $EventsType)
    if ($EventsDefinition.Filter.Count -gt 0) {
        foreach ($Entry in $EventsDefinition.Filter.Keys) {
            $EveryFilter = $EventsDefinition.Filter[$Entry]
            $StrippedFilter = $Entry -replace '#[0-9]{1,2}', ''
            [Array] $Splitter = $StrippedFilter.Split('#')
            if ($Splitter.Count -gt 1) {
                $PropertyName = $Splitter[0]
                $Operator = $Splitter[1]
            } else {
                $PropertyName = $StrippedFilter
                $Operator = 'eq'
            }
            $Events = foreach ($V in $EveryFilter) { foreach ($_ in $Events) { if ($Operator -eq 'eq') { if ($_.$PropertyName -eq $V) { $_ } } elseif ($Operator -eq 'like') { if ($_.$PropertyName -like $V) { $_ } } elseif ($Operator -eq 'ne') { if ($_.$PropertyName -ne $V) { $_ } } elseif ($Operator -eq 'gt') { if ($_.$PropertyName -gt $V) { $_ } } elseif ($Operator -eq 'lt') { if ($_.$PropertyName -lt $V) { $_ } } } }
        }
    }
    if ($EventsDefinition.FilterOr.Count -gt 0) {
        $Events = foreach ($_ in $Events) {
            foreach ($Entry in $EventsDefinition.FilterOr.Keys) {
                $StrippedFilter = $Entry -replace '#[0-9]{1,2}', ''
                [Array] $Splitter = $StrippedFilter.Split('#')
                if ($Splitter.Count -gt 1) {
                    $PropertyName = $Splitter[0]
                    $Operator = $Splitter[1]
                } else {
                    $PropertyName = $StrippedFilter
                    $Operator = 'eq'
                }
                $Value = $EventsDefinition.FilterOr[$Entry]
                foreach ($V in $Value) { if ($Operator -eq 'eq') { if ($_.$PropertyName -eq $V) { $_ } } elseif ($Operator -eq 'like') { if ($_.$PropertyName -like $V) { $_ } } elseif ($Operator -eq 'ne') { if ($_.$PropertyName -ne $V) { $_ } } elseif ($Operator -eq 'gt') { if ($_.$PropertyName -gt $V) { $_ } } elseif ($Operator -eq 'lt') { if ($_.$PropertyName -lt $V) { $_ } } }
            }
        }
    }
    $MyValue = foreach ($Event in $Events) {
        if (($Event.LogName -eq $EventsType) -and ($Event.ID -in $EventIDs)) { } else { continue }
        $HashTable = [ordered] @{ }
        foreach ($EventProperty in $Event.PSObject.Properties) {
            if ($null -ne $EventsDefinition.Ignore) { if ($EventsDefinition.Ignore.Contains($EventProperty.Name)) { if ($EventProperty.Value -like $EventsDefinition.Ignore[$EventProperty.Name]) { continue } } }
            if ($null -ne $EventsDefinition.Functions) {
                if ($EventsDefinition.Functions.Contains($EventProperty.Name)) {
                    if ($EventsDefinition.Functions[$EventProperty.Name] -contains 'Remove-WhiteSpace') { $EventProperty.Value = Remove-WhiteSpace -Text $EventProperty.Value }
                    if ($EventsDefinition.Functions[$EventProperty.Name] -contains 'Split-OnSpace') { $EventProperty.Value = $EventProperty.Value -Split ' ' }
                    if ($EventsDefinition.Functions[$EventProperty.Name] -contains 'Convert-UAC') { $EventProperty.Value = Convert-UAC -UAC $EventProperty.Value -Separator ', ' }
                    if ($EventsDefinition.Functions[$EventProperty.Name] -contains 'ConvertFrom-OperationType') { $EventProperty.Value = ConvertFrom-OperationType -OperationType $EventProperty.Value }
                    if ($EventsDefinition.Functions[$EventProperty.Name] -contains 'Clean-IpAddress') { $EventProperty.Value = if ($EventProperty.Value -match "::1") { 'localhost' } else { $EventProperty.Value } }
                }
            }
            if ($null -ne $EventsDefinition.Fields -and $EventsDefinition.Fields.Contains($EventProperty.Name)) { $HashTable[$EventsDefinition.Fields[$EventProperty.Name]] = $EventProperty.Value } else { $HashTable[$EventProperty.Name] = $EventProperty.Value }
        }
        if ($null -ne $EventsDefinition.Overwrite) {
            foreach ($Entry in $EventsDefinition.Overwrite.Keys) {
                [Array] $OverwriteObject = $EventsDefinition.Overwrite.$Entry
                $StrippedFilter = $Entry -replace '#[0-9]{1,2}', ''
                [Array] $Splitter = $StrippedFilter.Split('#')
                if ($Splitter.Count -gt 1) {
                    $PropertyName = $Splitter[0]
                    $Operator = $Splitter[1]
                } else {
                    $PropertyName = $StrippedFilter
                    $Operator = 'eq'
                }
                if ($OverwriteObject.Count -eq 3) { if ($Operator -eq 'eq') { if ($HashTable[($OverwriteObject[0])] -eq $OverwriteObject[1]) { $HashTable[$PropertyName] = $OverwriteObject[2] } } elseif ($Operator -eq 'ne') { } elseif ($Operator -eq 'like') { } elseif ($Operator -eq 'gt') { } elseif ($Operator -eq 'lt') { } } elseif ($OverwriteObject.Count -eq 4) { if ($Operator -eq 'eq') { if ($HashTable[($OverwriteObject[0])] -eq $OverwriteObject[1]) { $HashTable[$PropertyName] = $OverwriteObject[2] } else { $HashTable[$PropertyName] = $OverwriteObject[3] } } elseif ($Operator -eq 'ne') { } elseif ($Operator -eq 'like') { } elseif ($Operator -eq 'gt') { } elseif ($Operator -eq 'lt') { } } elseif ($OverwriteObject.Couint -eq 1) { $HashTable[$PropertyName] = $HashTable[($OverwriteObject[0])] }
            }
        }
        if ($null -ne $EventsDefinition.OverwriteByField) {
            foreach ($Entry in $EventsDefinition.OverwriteByField.Keys) {
                [Array] $OverwriteObject = $EventsDefinition.OverwriteByField.$Entry
                $StrippedFilter = $Entry -replace '#[0-9]{1,2}', ''
                [Array] $Splitter = $StrippedFilter.Split('#')
                if ($Splitter.Count -gt 1) {
                    $PropertyName = $Splitter[0]
                    $Operator = $Splitter[1]
                } else {
                    $PropertyName = $StrippedFilter
                    $Operator = 'eq'
                }
                if ($OverwriteObject.Count -eq 3) { if ($Operator -eq 'eq') { if ($HashTable[($OverwriteObject[0])] -eq $OverwriteObject[1]) { $HashTable[$PropertyName] = $HashTable[($OverwriteObject[2])] } } elseif ($Operator -eq 'ne') { if ($HashTable[($OverwriteObject[0])] -ne $OverwriteObject[1]) { $HashTable[$PropertyName] = $HashTable[($OverwriteObject[2])] } } elseif ($Operator -eq 'like') { } elseif ($Operator -eq 'gt') { } elseif ($Operator -eq 'lt') { } } elseif ($OverwriteObject.Count -eq 4) { if ($Operator -eq 'eq') { if ($HashTable[($OverwriteObject[0])] -eq $OverwriteObject[1]) { $HashTable[$PropertyName] = $HashTable[($OverwriteObject[2])] } else { $HashTable[$PropertyName] = $HashTable[($OverwriteObject[3])] } } elseif ($Operator -eq 'ne') { } elseif ($Operator -eq 'like') { } elseif ($Operator -eq 'gt') { } elseif ($Operator -eq 'lt') { } } elseif ($OverwriteObject.Count -eq 1) { $HashTable[$PropertyName] = $HashTable[($OverwriteObject[0])] }
            }
        }
        [PsCustomObject]$HashTable
    }
    $MyValue = Find-EventsTo -Ignore -Events $MyValue -DataSet $EventsDefinition.IgnoreWords
    if ($null -eq $EventsDefinition.Fields) { return $MyValue | Sort-Object $EventsDefinition.SortBy } else { return $MyValue | Select-Object @($EventsDefinition.Fields.Values) | Sort-Object $EventsDefinition.SortBy }
}
function Export-ReportToCSV {
    [CmdletBinding()]
    param ([bool] $Report,
        [System.Collections.IDictionary] $ReportOptions,
        [string] $Extension,
        [string] $ReportName,
        [Array] $ReportTable)
    if ($Report) {
        $ReportFilePath = Set-ReportFileName -ReportOptions $ReportOptions -ReportExtension $Extension -ReportName $ReportName
        if ($ReportTable.Count -gt 0) { $ReportTable | Export-Csv -Encoding Unicode -Path $ReportFilePath }
        return $ReportFilePath
    } else { return '' }
}
function Export-ReportToHTML {
    param ($Report,
        $ReportTable,
        $ReportTableText,
        [switch] $Special)
    if ($Report -eq $true) {
        if ($special) { return Set-EmailBodyPreparedTable -TableData $ReportTable -TableWelcomeMessage $ReportTableText }
        return Set-EmailBody -TableData $ReportTable -TableWelcomeMessage $ReportTableText
    } else { return '' }
}
function Export-ReportToSQL {
    [CmdletBinding()]
    param ([System.Collections.IDictionary] $Report,
        [System.Collections.IDictionary] $ReportOptions,
        [string] $ReportName,
        [Array] $ReportTable)
    if ($Report.Enabled) {
        if ($ReportOptions.Contains('AsSql') -and $ReportOptions.AsSql.Use) {
            if ($Report.Contains('EnabledSqlGlobal') -and $Report.EnabledSqlGlobal) {
                $Logger.AddInfoRecord("Sending $ReportName to SQL at Global level")
                $SqlQuery = Send-SqlInsert -Object $ReportTable -SqlSettings $ReportOptions.AsSql -Verbose:$ReportOptions.Debug.Verbose
                foreach ($Query in $SqlQuery) { $Logger.AddInfoRecord("MS SQL Output: $Query") }
            }
        }
        if ($Report.Contains('ExportToSql') -and $Report.ExportToSql.Use) {
            $Logger.AddInfoRecord("Sending $ReportName to SQL at Local level")
            $SqlQuery = Send-SqlInsert -Object $ReportTable -SqlSettings $Report.ExportToSql -Verbose:$ReportOptions.Debug.Verbose
            foreach ($Query in $SqlQuery) { $Logger.AddInfoRecord("MS SQL Output: $Query") }
        }
    }
}
function Export-ReportToXLSX {
    [CmdletBinding()]
    param([bool] $Report,
        [System.Collections.IDictionary] $ReportOptions,
        [string] $ReportFilePath,
        [string] $ReportName,
        [Array] $ReportTable)
    if (($Report -eq $true) -and ($($ReportTable | Measure-Object).Count -gt 0)) { $ReportTable | ConvertTo-Excel -Path $ReportFilePath -WorkSheetname $ReportName -AutoSize -FreezeTopRow -AutoFilter }
}
function Export-ToCSV {
    [CmdletBinding()]
    param ([bool] $Report,
        [string] $Path,
        [string] $FilePattern,
        [string] $DateFormat,
        [string] $ReportName,
        [Array] $ReportTable)
    if ($Report) {
        $ReportFileName = Set-ReportFile -Path $Path -FileNamePattern $FilePattern -DateFormat $DateFormat -ReportName $ReportName
        try {
            if ($ReportTable.Count -gt 0) { $ReportTable | Export-Csv -Encoding Unicode -LiteralPath $ReportFileName -ErrorAction Stop -Force }
            return $ReportFileName
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            $Logger.AddErrorRecord("Error saving file $ReportFileName.")
            $Logger.AddErrorRecord("Error: $ErrorMessage")
            return ''
        }
    } else { return '' }
}
function Export-ToSQL {
    [CmdletBinding()]
    param ([System.Collections.IDictionary] $Report,
        [System.Collections.IDictionary] $ReportOptions,
        [string] $ReportName,
        [Array] $ReportTable)
    if ($Report.Enabled) {
        if ($ReportOptions.Contains('AsSql') -and $ReportOptions.AsSql.Enabled -and $Report.Contains('SqlExport') -and $Report.SqlExport.EnabledGlobal) {
            $Logger.AddInfoRecord("Sending $ReportName to SQL at Global level")
            $SqlQuery = Send-SqlInsert -Object $ReportTable -SqlSettings $ReportOptions.AsSql -Verbose:$ReportOptions.Debug.Verbose
            foreach ($Query in $SqlQuery) { $Logger.AddInfoRecord("MS SQL GLOBAL Output: $Query") }
        }
        if ($Report.Contains('SqlExport') -and $Report.SqlExport.Enabled) {
            $Logger.AddInfoRecord("Sending $ReportName to SQL at Local level")
            $SqlQuery = Send-SqlInsert -Object $ReportTable -SqlSettings $Report.SqlExport -Verbose:$ReportOptions.Debug.Verbose
            foreach ($Query in $SqlQuery) { $Logger.AddInfoRecord("MS SQL LOCAL Output: $Query") }
        }
    }
}
function Get-NotificationParameters {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Notifications,
        [string] $ActivityTitle,
        [string] $Priority,
        [string] $Type)
    $Object = @{Uri       = ''
        ActivityImageLink = ''
        Color             = ''
        AvatarImage       = ''
        AvatarName        = ''
    }
    if ($null -ne $Notifications.$Priority) {
        $Logger.AddInfoRecord("Service $Type is using $Priority priority Event on $ActivityTitle")
        $Option = $Priority
    } else {
        $Logger.AddInfoRecord("Service $Type is using Default priority Event on $ActivityTitle")
        $Option = 'Default'
    }
    $Object.Uri = $Notifications[$Option].Uri
    $Object.AvatarName = $Notifications[$Option].AvatarName
    $Object.AvatarImage = $Notifications[$Option].AvatarImage
    $Object.ActivityImageLink = $Notifications[$Option].ActivityLinks.Default.Link
    $Object.Color = $Notifications[$Option].ActivityLinks.Default.Color
    foreach ($Type in $Notifications[$option].ActivityLinks.Keys | Where-Object { $_ -ne 'Default' }) {
        if ($ActivityTitle -like "*$Type*") {
            $Object.ActivityImageLink = $Notifications[$Option].ActivityLinks.$Type.Link
            $Object.Color = $Notifications[$Option].ActivityLinks.$Type.Color
            break
        }
    }
    return $Object
}
function Send-Notificaton {
    [CmdletBinding()]
    param([PSCustomObject] $Events,
        [Parameter(Mandatory = $true)][alias('ReportOptions')][System.Collections.IDictionary] $Options,
        [string] $Priority = 'Default')
    Begin { }
    Process {
        if ($Events -ne $null) {
            foreach ($Event in $Events) {
                [string] $MessageTitle = 'Active Directory Changes'
                [string] $ActivityTitle = $($Event.Action).Trim()
                $Teams = Get-NotificationParameters -Type 'Microsoft Teams' -Notifications $Options.Notifications.MicrosoftTeams -ActivityTitle $ActivityTitle -Priority $Priority
                $Slack = Get-NotificationParameters -Type 'Slack' -Notifications $Options.Notifications.Slack -ActivityTitle $ActivityTitle -Priority $Priority
                $Discord = Get-NotificationParameters -Type 'Discord' -Notifications $Options.Notifications.Discord -ActivityTitle $ActivityTitle -Priority $Priority
                $FactsSlack = @()
                $FactsTeams = @()
                $FactsDiscord = @()
                foreach ($Property in $event.PSObject.Properties) {
                    if ($null -ne $Property.Value -and $Property.Value -ne '') {
                        if ($Property.Name -eq 'When') {
                            $FactsTeams += New-TeamsFact -Name $Property.Name -Value $Property.Value.DateTime
                            $FactsSlack += @{title = $Property.Name; value = $Property.Value.DateTime; short = $true }
                            $FactsDiscord += New-DiscordFact -Name $Property.Name -Value $Property.Value.DateTime -Inline $true
                        } else {
                            $FactsTeams += New-TeamsFact -Name $Property.Name -Value $Property.Value
                            $FactsSlack += @{title = $Property.Name; value = $Property.Value; short = $true }
                            $FactsDiscord += New-DiscordFact -Name $Property.Name -Value $Property.Value -Inline $true
                        }
                    }
                }
                if ($Options.Notifications.Slack.Enabled) {
                    $SlackChannel = $Options.Notifications.Slack.$Priority.Channel
                    $SlackColor = ConvertFrom-Color -Color $Slack.Color
                    $Data = New-SlackMessageAttachment -Color $SlackColor -Title "$MessageTitle - $ActivityTitle" -Fields $FactsSlack -Fallback $ActivityTitle |
                        New-SlackMessage -Channel $SlackChannel -IconEmoji :bomb: |
                            Send-SlackMessage -Uri $Slack.Uri -Verbose
                    Write-Color @script:WriteParameters -Text "[i] Slack output: ", $Data -Color White, Yellow
                }
                if ($Options.Notifications.MicrosoftTeams.Enabled) {
                    $Section1 = New-TeamsSection -ActivityTitle $ActivityTitle -ActivityImageLink $Teams.ActivityImageLink -ActivityDetails $FactsTeams
                    $Data = Send-TeamsMessage -URI $Teams.Uri -MessageTitle $MessageTitle -Color $Teams.Color -Sections $Section1 -Supress $false -MessageSummary $ActivityTitle
                    Write-Color @script:WriteParameters -Text "[i] Teams output: ", $Data -Color White, Yellow
                }
                if ($Options.Notifications.Discord.Enabled) {
                    $Thumbnail = New-DiscordImage -Url $Discord.ActivityImageLink
                    $Section1 = New-DiscordSection -Title $ActivityTitle -Facts $FactsDiscord -Thumbnail $Thumbnail -Color $Discord.Color -Verbose
                    $Data = Send-DiscordMessage -WebHookUrl $Discord.Uri -Sections $Section1 -AvatarName $Discord.AvatarName -AvatarUrl $Discord.AvatarUrl -OutputJSON
                    Write-Color @script:WriteParameters -Text "[i] Discord output: ", $Data -Color White, Yellow
                }
                if ($Options.Notifications.MSSQL.Enabled) {
                    $SqlQuery = Send-SqlInsert -Object $Event -SqlSettings $Options.Notifications.MSSQL -Verbose:$Options.Debug.Verbose
                    foreach ($Query in $SqlQuery) { Write-Color @script:WriteParameters -Text '[i] ', 'MS SQL Output: ', $Query -Color White, White, Yellow }
                }
                if ($Options.Notifications.Email.Enabled) {
                    if ($Options.Notifications.Email.AsHTML.Enabled) {
                        $Logger.AddInfoRecord('Prepare email head and body')
                        $HtmlHead = Set-EmailHead -FormattingOptions $Options.Notifications.Email.AsHTML.Formatting
                        $HtmlBody = Set-EmailReportBranding -FormattingParameters $Options.Notifications.Email.AsHTML.Formatting
                        $HtmlBody += Export-ReportToHTML -Report $true -ReportTable $Event -ReportTableText "Quick notification event"
                        $HtmlBody = Set-EmailFormatting -Template $HtmlBody -FormattingParameters $Options.Notifications.Email.AsHTML.Formatting -ConfigurationParameters $Options -Logger $Logger -SkipNewLines
                        $HTML = $HtmlHead + $HtmlBody
                        $EmailBody = $HTML
                        $ReportHTMLPath = Set-ReportFile -Path $Env:TEMP -FileNamePattern 'PSWinReporting.html' -DateFormat $null
                        try {
                            $HTML | Out-File -Encoding Unicode -FilePath $ReportHTMLPath -ErrorAction Stop
                            $Logger.AddInfoRecord("Saving report to file: $ReportHTMLPath")
                            if ($Options.SendMail.Attach.HTML) {
                                $AttachHTML += $ReportHTMLPath
                                $AttachedReports += $ReportHTMLPath
                            }
                        } catch {
                            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                            $Logger.AddErrorRecord("Error saving file $ReportHTMLPath.")
                            $Logger.AddErrorRecord("Error: $ErrorMessage")
                        }
                        $TemporarySubject = $Options.Notifications.Email.$Priority.Parameters.Subject
                        $Logger.AddInfoRecord('Sending email with reports')
                        if ($Options.Notifications.Email.AsHTML.Formatting.CompanyBranding.Inline) { $SendMail = Send-Email -EmailParameters $Options.Notifications.Email.$Priority.Parameters -Body $EmailBody -Attachment $AttachedReports -Subject $TemporarySubject -InlineAttachments @{logo = $Options.Notifications.Email.AsHTML.Formatting.CompanyBranding.Logo } -Logger $Logger } else { $SendMail = Send-Email -EmailParameters $Options.Notifications.Email.$Priority.Parameters -Body $EmailBody -Attachment $AttachedReports -Subject $TemporarySubject -Logger $Logger }
                        if ($SendMail.Status) { $Logger.AddInfoRecord('Email successfully sent') } else { $Logger.AddInfoRecord("Error sending message: $($SendMail.Error)") }
                        Remove-ReportsFiles -KeepReports $false -ReportFiles $ReportHTMLPath
                    }
                }
            }
        }
    }
    End { }
}
function Start-ReportSpecial {
    [CmdletBinding()]
    param ([System.Collections.IDictionary] $Dates,
        [alias('ReportOptions')][System.Collections.IDictionary] $Options,
        [alias('ReportDefinitions')][System.Collections.IDictionary] $Definitions,
        [alias('Servers', 'Computers')][System.Collections.IDictionary] $Target)
    $Verbose = ($PSCmdlet.MyInvocation.BoundParameters['Verbose'] -eq $true)
    $Time = Start-TimeLog
    $AttachedReports = @()
    $AttachXLSX = @()
    $AttachHTML = @()
    $AttachDynamicHTML = @()
    $AttachCSV = @()
    [Array] $ExtendedInput = Get-ServersList -Definitions $Definitions -Target $Target
    foreach ($Entry in $ExtendedInput) { if ($Entry.Type -eq 'Computer') { $Logger.AddInfoRecord("Computer $($Entry.Server) added to scan $($Entry.LogName) log for events: $($Entry.EventID -join ', ')") } else { $Logger.AddInfoRecord("File $($Entry.Server) added to scan $($Entry.LogName) log for events: $($Entry.EventID -join ', ')") } }
    [Array] $AllEvents = Get-Events -ExtendedInput $ExtendedInput -ErrorAction SilentlyContinue -ErrorVariable AllErrors -Verbose:$Verbose
    $Logger.AddInfoRecord("Found $($AllEvents.Count) events.")
    foreach ($Errors in $AllErrors) { $Logger.AddErrorRecord($Errors) }
    if ($Options.RemoveDuplicates.Enabled) {
        $Logger.AddInfoRecord("Removing Duplicates from all events. Current list contains $($AllEvents.Count) events")
        $AllEvents = Remove-DuplicateObjects -Object $AllEvents -Property $Options.RemoveDuplicates.Properties
        $Logger.AddInfoRecord("Removed Duplicates - following $($AllEvents.Count) events will be analyzed further")
    }
    $Results = Get-EventsOutput -Definitions $Definitions -AllEvents $AllEvents
    if ($Options.AsHTML.Enabled) {
        $Logger.AddInfoRecord('Prepare email head and body')
        $HtmlHead = Set-EmailHead -FormattingOptions $Options.AsHTML.Formatting
        $HtmlBody = Set-EmailReportBranding -FormattingParameters $Options.AsHTML.Formatting
        $HtmlBody += Set-EmailReportDetails -FormattingParameters $Options.AsHTML.Formatting -Dates $Dates -Warnings $Warnings
        foreach ($ReportName in $Definitions.Keys) {
            $ReportNameTitle = Format-AddSpaceToSentence -Text $ReportName -ToLowerCase
            $HtmlBody += Export-ReportToHTML -Report $Definitions.$ReportName.Enabled -ReportTable $Results.$ReportName -ReportTableText "Following $ReportNameTitle happened"
        }
        $HtmlBody = Set-EmailWordReplacements -Body $HtmlBody -Replace '**TimeToGenerateDays**' -ReplaceWith $time.Elapsed.Days
        $HtmlBody = Set-EmailWordReplacements -Body $HtmlBody -Replace '**TimeToGenerateHours**' -ReplaceWith $time.Elapsed.Hours
        $HtmlBody = Set-EmailWordReplacements -Body $HtmlBody -Replace '**TimeToGenerateMinutes**' -ReplaceWith $time.Elapsed.Minutes
        $HtmlBody = Set-EmailWordReplacements -Body $HtmlBody -Replace '**TimeToGenerateSeconds**' -ReplaceWith $time.Elapsed.Seconds
        $HtmlBody = Set-EmailWordReplacements -Body $HtmlBody -Replace '**TimeToGenerateMilliseconds**' -ReplaceWith $time.Elapsed.Milliseconds
        $HtmlBody = Set-EmailFormatting -Template $HtmlBody -FormattingParameters $Options.AsHTML.Formatting -ConfigurationParameters $Options -Logger $Logger -SkipNewLines
        $HTML = $HtmlHead + $HtmlBody
        $ReportHTMLPath = Set-ReportFile -Path $Options.AsHTML.Path -FileNamePattern $Options.AsHTML.FilePattern -DateFormat $Options.AsHTML.DateFormat
        try {
            $HTML | Out-File -Encoding Unicode -FilePath $ReportHTMLPath -ErrorAction Stop
            $Logger.AddInfoRecord("Saving report to file: $ReportHTMLPath")
            if ($Options.SendMail.Attach.HTML) {
                $AttachHTML += $ReportHTMLPath
                $AttachedReports += $ReportHTMLPath
            }
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            $Logger.AddErrorRecord("Error saving file $ReportHTMLPath.")
            $Logger.AddErrorRecord("Error: $ErrorMessage")
        }
    }
    if ($Options.AsDynamicHTML.Enabled) {
        $DynamicHTMLPath = Set-ReportFile -Path $Options.AsDynamicHTML.Path -FileNamePattern $Options.AsDynamicHTML.FilePattern -DateFormat $Options.AsDynamicHTML.DateFormat
        $null = New-HTML -TitleText $Options.AsDynamicHTML.Title -UseCssLinks:$Options.AsDynamicHTML.EmbedCSS -UseJavaScriptLinks:$Options.AsDynamicHTML.EmbedJS { foreach ($ReportName in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled', 'SqlExport' }) {
                $ReportNameTitle = Format-AddSpaceToSentence -Text $ReportName
                if ($Definitions.$ReportName.Enabled) { New-HTMLSection -HeaderText $ReportNameTitle -CanCollapse { New-HTMLPanel { if ($null -ne $Results.$ReportName) { New-HTMLTable -DataTable $Results.$ReportName -HideFooter } } } }
            } } -FilePath $DynamicHTMLPath
        try {
            if ($Options.SendMail.Attach.DynamicHTML) {
                $AttachDynamicHTML += $DynamicHTMLPath
                $AttachedReports += $DynamicHTMLPath
            }
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            $Logger.AddErrorRecord("Error saving file $ReportHTMLPath.")
            $Logger.AddErrorRecord("Error: $ErrorMessage")
        }
    }
    if ($Options.AsExcel.Enabled) {
        $Logger.AddInfoRecord('Prepare Microsoft Excel (.XLSX) file with Events')
        $ReportFilePathXLSX = Set-ReportFile -Path $Options.AsExcel.Path -FileNamePattern $Options.AsExcel.FilePattern -DateFormat $Options.AsExcel.DateFormat
        foreach ($ReportName in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled', 'SqlExport' }) {
            $ReportNameTitle = Format-AddSpaceToSentence -Text $ReportName
            Export-ReportToXLSX -Report $Definitions.$ReportName.Enabled -ReportOptions $Options -ReportFilePath $ReportFilePathXLSX -ReportName $ReportNameTitle -ReportTable $Results.$ReportName
        }
        if ($Options.SendMail.Attach.XLSX) {
            $AttachXLSX += $ReportFilePathXLSX
            $AttachedReports += $ReportFilePathXLSX
        }
    }
    if ($Options.AsCSV.Enabled) {
        $ReportFilePathCSV = @()
        $Logger.AddInfoRecord('Prepare CSV files with Events')
        foreach ($ReportName in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled', 'SqlExport' }) { $ReportFilePathCSV += Export-ToCSV -Report $Definitions.$ReportName.Enabled -ReportName $ReportName -ReportTable $Results.$ReportName -Path $Options.AsCSV.Path -FilePattern $Options.AsCSV.FilePattern -DateFormat $Options.AsCSV.DateFormat }
        if ($Options.SendMail.Attach.CSV) {
            $AttachCSV += $ReportFilePathCSV
            $AttachedReports += $ReportFilePathCSV
        }
    }
    if ($Options.AsHTML.Enabled -and $Options.AsHTML.OpenAsFile) {
        try { if ($ReportHTMLPath -ne '' -and (Test-Path -LiteralPath $ReportHTMLPath)) { Invoke-Item -LiteralPath $ReportHTMLPath } } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            $Logger.AddErrorRecord("Error opening file $ReportHTMLPath.")
        }
    }
    if ($Options.AsDynamicHTML.Enabled -and $Options.AsDynamicHTML.OpenAsFile) {
        try { if ($DynamicHTMLPath -ne '' -and (Test-Path -LiteralPath $DynamicHTMLPath)) { Invoke-Item -LiteralPath $DynamicHTMLPath } } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            $Logger.AddErrorRecord("Error opening file $DynamicHTMLPath.")
        }
    }
    if ($Options.AsExcel.Enabled -and $Options.AsExcel.OpenAsFile) {
        try { if ($ReportFilePathXLSX -ne '' -and (Test-Path -LiteralPath $ReportFilePathXLSX)) { Invoke-Item -LiteralPath $ReportFilePathXLSX } } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            $Logger.AddErrorRecord("Error opening file $ReportFilePathXLSX.")
        }
    }
    if ($Options.AsCSV.Enabled -and $Options.AsCSV.OpenAsFile) {
        foreach ($CSV in $AttachCSV) {
            try { if ($CSV -ne '' -and (Test-Path -LiteralPath $CSV)) { Invoke-Item -LiteralPath $CSV } } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                $Logger.AddErrorRecord("Error opening file $CSV.")
            }
        }
    }
    $AttachedReports = $AttachedReports | Sort-Object -Unique
    if ($Options.SendMail.Enabled) {
        foreach ($Report in $AttachedReports) { $Logger.AddInfoRecord("Following files will be attached to email $Report") }
        if ($Options.SendMail.InlineHTML) { $EmailBody = $HTML } else { $EmailBody = '' }
        $TemporarySubject = $Options.SendMail.Parameters.Subject -replace "<<DateFrom>>", "$($Dates.DateFrom)" -replace "<<DateTo>>", "$($Dates.DateTo)"
        $Logger.AddInfoRecord('Sending email with reports')
        if ($Options.AsHTML.Formatting.CompanyBranding.Inline) { $SendMail = Send-Email -EmailParameters $Options.SendMail.Parameters -Body $EmailBody -Attachment $AttachedReports -Subject $TemporarySubject -InlineAttachments @{logo = $Options.AsHTML.Formatting.CompanyBranding.Logo } -Logger $Logger } else { $SendMail = Send-Email -EmailParameters $Options.SendMail.Parameters -Body $EmailBody -Attachment $AttachedReports -Subject $TemporarySubject -Logger $Logger }
        if ($SendMail.Status) { $Logger.AddInfoRecord('Email successfully sent') } else { $Logger.AddErrorRecord("Error sending message: $($SendMail.Error)") }
        Remove-ReportsFiles -KeepReports $Options.SendMail.KeepReports.XLSX -ReportFiles $AttachXLSX
        Remove-ReportsFiles -KeepReports $Options.SendMail.KeepReports.CSV -ReportFiles $AttachCSV
        Remove-ReportsFiles -KeepReports $Options.SendMail.KeepReports.HTML -ReportFiles $AttachHTML
        Remove-ReportsFiles -KeepReports $Options.SendMail.KeepReports.DynamicHTML -ReportFiles $AttachDynamicHTML
    }
    foreach ($ReportName in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled', 'SqlExport' }) {
        $ReportNameTitle = Format-AddSpaceToSentence -Text $ReportName
        Export-ToSql -Report $Definitions.$ReportName -ReportOptions $Options -ReportName $ReportNameTitle -ReportTable $Results.$ReportName
    }
    $ElapsedTime = Stop-TimeLog -Time $Time
    $Logger.AddInfoRecord("Time to finish $ElapsedTime")
}
function Get-ChoosenDates {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $ReportTimes)
    $Dates = @(if ($ReportTimes.Contains('PastHour') -and $ReportTimes.PastHour.Enabled) {
            $DatesPastHour = Find-DatesPastHour
            if ($DatesPastHour) { $DatesPastHour }
        }
        if ($ReportTimes.Contains('CurrentHour') -and $ReportTimes.CurrentHour.Enabled) {
            $DatesCurrentHour = Find-DatesCurrentHour
            if ($DatesCurrentHour) { $DatesCurrentHour }
        }
        if ($ReportTimes.Contains('PastDay') -and $ReportTimes.PastDay.Enabled) {
            $DatesDayPrevious = Find-DatesDayPrevious
            if ($DatesDayPrevious) { $DatesDayPrevious }
        }
        if ($ReportTimes.Contains('CurrentDay') -and $ReportTimes.CurrentDay.Enabled) {
            $DatesDayToday = Find-DatesDayToday
            if ($DatesDayToday) { $DatesDayToday }
        }
        if ($ReportTimes.Contains('OnDay') -and $ReportTimes.OnDay.Enabled) {
            foreach ($Day in $ReportTimes.OnDay.Days) {
                $DatesReportOnDay = Find-DatesPastWeek $Day
                if ($DatesReportOnDay) { $DatesReportOnDay }
            }
        }
        if ($ReportTimes.Contains('PastMonth') -and $ReportTimes.PastMonth.Enabled) {
            $DatesMonthPrevious = Find-DatesMonthPast -Force $ReportTimes.PastMonth.Force
            if ($DatesMonthPrevious) { $DatesMonthPrevious }
        }
        if ($ReportTimes.Contains('CurrentMonth') -and $ReportTimes.CurrentMonth.Enabled) {
            $DatesMonthCurrent = Find-DatesMonthCurrent
            if ($DatesMonthCurrent) { $DatesMonthCurrent }
        }
        if ($ReportTimes.Contains('PastQuarter') -and $ReportTimes.PastQuarter.Enabled) {
            $DatesQuarterLast = Find-DatesQuarterLast -Force $ReportTimes.PastQuarter.Force
            if ($DatesQuarterLast) { $DatesQuarterLast }
        }
        if ($ReportTimes.Contains('CurrentQuarter') -and $ReportTimes.CurrentQuarter.Enabled) {
            $DatesQuarterCurrent = Find-DatesQuarterCurrent
            if ($DatesQuarterCurrent) { $DatesQuarterCurrent }
        }
        if ($ReportTimes.Contains('CurrentDayMinusDayX') -and $ReportTimes.CurrentDayMinusDayX.Enabled) {
            $DatesCurrentDayMinusDayX = Find-DatesCurrentDayMinusDayX $ReportTimes.CurrentDayMinusDayX.Days
            if ($DatesCurrentDayMinusDayX) { $DatesCurrentDayMinusDayX }
        }
        if ($ReportTimes.Contains('CurrentDayMinuxDaysX') -and $ReportTimes.CurrentDayMinuxDaysX.Enabled) {
            $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX $ReportTimes.CurrentDayMinuxDaysX.Days
            if ($DatesCurrentDayMinusDaysX) { $DatesCurrentDayMinusDaysX }
        }
        if ($ReportTimes.Contains('CustomDate') -and $ReportTimes.CustomDate.Enabled) {
            $DatesCustom = @{DateFrom = $ReportTimes.CustomDate.DateFrom
                DateTo                = $ReportTimes.CustomDate.DateTo
            }
            if ($DatesCustom) { $DatesCustom }
        }
        if ($ReportTimes.Contains('Everything') -and $ReportTimes.Everything.Enabled) {
            $DatesEverything = @{DateFrom = Get-Date -Year 1600 -Month 1 -Day 1
                DateTo                    = Get-Date -Year 2300 -Month 1 -Day 1
            }
            $DatesEverything
        }
        if ($ReportTimes.Contains('Last3days') -and $ReportTimes.Last3days.Enabled) {
            $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX -days 3
            if ($DatesCurrentDayMinusDaysX) { $DatesCurrentDayMinusDaysX }
        }
        if ($ReportTimes.Contains('Last7days') -and $ReportTimes.Last7days.Enabled) {
            $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX -days 7
            if ($DatesCurrentDayMinusDaysX) { $DatesCurrentDayMinusDaysX }
        }
        if ($ReportTimes.Contains('Last14days') -and $ReportTimes.Last14days.Enabled) {
            $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX -days 14
            if ($DatesCurrentDayMinusDaysX) { $DatesCurrentDayMinusDaysX }
        })
    return $Dates
}
function Get-DatesDefinitions {
    [CmdletBinding()]
    param([string[]] $Skip)
    $Times = foreach ($Key in $Script:ReportTimes.Keys) { if ($SkipTime -notcontains $Key) { $Key } }
    $Times
}
function Get-EventLogFileList {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Sections)
    $EventFiles = @(if ($Sections.Contains("Directories")) {
            foreach ($Folder in $Sections.Directories.Keys) {
                $Files = Get-FilesInFolder -Folder $Sections.Directories.$Folder -Extension '*.evtx'
                foreach ($File in $Files) { $File }
            }
        }
        if ($Sections.Contains("Files")) {
            foreach ($FileName in $Sections.Files.Keys) {
                $File = $($Sections.Files.$FileName)
                if ($File -and (Test-Path -LiteralPath $File)) { $File } else { if (-not $Quiet) { $Logger.AddErrorRecord("File $File doesn't exists. Skipping for scan.") } }
            }
        })
    return $EventFiles | Sort-Object -Unique
}
function Get-EventsDefinitions {
    [CmdLetBinding()]
    param([System.Collections.IDictionary] $Definitions)
    [string] $ConfigurationPath = "$Env:ALLUSERSPROFILE\Evotec\PSWinReporting\Definitions"
    try { $Files = Get-ChildItem -LiteralPath $ConfigurationPath -Filter '*.xml' -ErrorAction Stop } catch { $Files = $null }
    $AllDefinitions = $Script:ReportDefinitions
    if ($null -ne $Files) {
        try {
            foreach ($File in $Files) { $AllDefinitions += Import-Clixml -LiteralPath $File.FullName }
            if ($Definitions) { $AllDefinitions += $Definitions }
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($ErrorMessage -like '*Item has already been added. Key in dictionary*') { Write-Warning "Get-EventsDefintions - Duplicate key in definition. Please make sure names in Hashtables are unique." } else { Write-Warning "Get-EventsDefintions - Error: $ErrorMessage" }
            $AllDefinitions = $null
        }
    }
    return $AllDefinitions
}
function Get-ServersList {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Definitions,
        [System.Collections.IDictionary] $Target,
        [System.Collections.IDictionary] $Dates,
        [switch] $Quiet,
        [string] $Who,
        [string] $Whom,
        [string] $NotWho,
        [string] $NotWhom)
    $ServersList = @(if ($Target.Servers.Enabled) {
            if (-not $Quiet) { $Logger.AddInfoRecord("Preparing servers list - defined list") }
            [Array] $Servers = foreach ($Server in $Target.Servers.Keys | Where-Object { $_ -ne 'Enabled' }) {
                if ($Target.Servers.$Server -is [System.Collections.IDictionary]) {
                    [ordered] @{ComputerName = $Target.Servers.$Server.ComputerName
                        LogName              = $Target.Servers.$Server.LogName
                    }
                } elseif ($Target.Servers.$Server -is [Array] -or $Target.Servers.$Server -is [string]) { $Target.Servers.$Server }
            }
            $Servers
        }
        if ($Target.DomainControllers.Enabled) {
            if (-not $Quiet) { $Logger.AddInfoRecord("Preparing servers list - domain controllers autodetection") }
            [Array] $Servers = (Get-WinADDomainControllers -SkipEmpty).HostName
            $Servers
        })
    if ($Target.LocalFiles.Enabled) {
        if (-not $Quiet) { $Logger.AddInfoRecord("Preparing file list - defined event log files") }
        $Files = Get-EventLogFileList -Sections $Target.LocalFiles
    }
    [Array] $LogNames = foreach ($Report in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled', 'SqlExport' }) { if ($Definitions.$Report.Enabled) { foreach ($SubReport in $Definitions.$Report.Keys | Where-Object { $_ -notcontains 'Enabled' }) { if ($Definitions.$Report.$SubReport.Enabled) { $Definitions.$Report.$SubReport.LogName } } } }
    if ($LogNames.Count -eq 0) {
        $Logger.AddErrorRecord("Definitions provided don't contain any enabled report or subevents within report. Please check your definitions and try again.")
        Exit
    }
    [Array] $ExtendedInput = foreach ($Log in $LogNames | Sort-Object -Unique) {
        $EventIDs = foreach ($Report in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled', 'SqlExport' }) { if ($Definitions.$Report.Enabled) { foreach ($SubReport in $Definitions.$Report.Keys | Where-Object { $_ -notcontains 'Enabled' }) { if ($Definitions.$Report.$SubReport.Enabled) { if ($Definitions.$Report.$SubReport.LogName -eq $Log) { $Definitions.$Report.$SubReport.Events } } } } }
        $NamedDataFilter = @{ }
        if ($Who -ne '') { $NamedDataFilter.SubjectUserName = $Who }
        if ($Whom -ne '') { $NamedDataFilter.TargetUserName = $Whom }
        $NamedDataExcludeFilter = @{ }
        if ($NotWho -ne '') { $NamedDataExcludeFilter.SubjectUserName = $NotWho }
        if ($NotWhom -ne '') { $NamedDataExcludeFilter.TargetUserName = $NotWhom }
        $OutputServers = foreach ($Server in $ServersList) {
            if ($Server -is [System.Collections.IDictionary]) {
                [PSCustomObject]@{Server   = $Server.ComputerName
                    LogName                = $Server.LogName
                    EventID                = $EventIDs | Sort-Object -Unique
                    Type                   = 'Computer'
                    DateFrom               = $Dates.DateFrom
                    DateTo                 = $Dates.DateTo
                    NamedDataFilter        = if ($NamedDataFilter.Count -ne 0) { $NamedDataFilter } else { }
                    NamedDataExcludeFilter = if ($NamedDataExcludeFilter.Count -ne 0) { $NamedDataExcludeFilter } else { }
                }
            } elseif ($Server -is [Array] -or $Server -is [string]) {
                foreach ($S in $Server) {
                    [PSCustomObject]@{Server   = $S
                        LogName                = $Log
                        EventID                = $EventIDs | Sort-Object -Unique
                        Type                   = 'Computer'
                        DateFrom               = $Dates.DateFrom
                        DateTo                 = $Dates.DateTo
                        NamedDataFilter        = if ($NamedDataFilter.Count -ne 0) { $NamedDataFilter } else { }
                        NamedDataExcludeFilter = if ($NamedDataExcludeFilter.Count -ne 0) { $NamedDataExcludeFilter } else { }
                    }
                }
            }
        }
        $OutputFiles = foreach ($File in $FIles) {
            [PSCustomObject]@{Server   = $File
                LogName                = $Log
                EventID                = $EventIDs | Sort-Object -Unique
                Type                   = 'File'
                DateFrom               = $Dates.DateFrom
                DateTo                 = $Dates.DateTo
                NamedDataFilter        = if ($NamedDataFilter.Count -ne 0) { $NamedDataFilter } else { }
                NamedDataExcludeFilter = if ($NamedDataExcludeFilter.Count -ne 0) { $NamedDataExcludeFilter } else { }
            }
        }
        $OutputServers
        $OutputFiles
    }
    if ($ExtendedInput.Count -gt 1) { $ExtendedInput } else { , $ExtendedInput }
}
function Get-ServersListLimited {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Target,
        [int64] $RecordID,
        [switch] $Quiet,
        [string] $Who,
        [string] $Whom,
        [string] $NotWho,
        [string] $NotWhom)
    if ($Target.Servers.Enabled) {
        if (-not $Quiet) { $Logger.AddInfoRecord("Preparing servers list - defined list") }
        [Array] $Servers = foreach ($Server in $Target.Servers.Keys | Where-Object { $_ -ne 'Enabled' }) {
            if ($Target.Servers.$Server -is [System.Collections.IDictionary]) {
                [ordered] @{ComputerName = $Target.Servers.$Server.ComputerName
                    LogName              = $Target.Servers.$Server.LogName
                }
            } elseif ($Target.Servers.$Server -is [Array] -or $Target.Servers.$Server -is [string]) { $Target.Servers.$Server }
        }
    }
    [Array] $ExtendedInput = foreach ($Server in $Servers) {
        [PSCustomObject] @{Server  = $Server.ComputerName
            LogName                = $Server.LogName
            RecordID               = $RecordID
            NamedDataFilter        = if ($NamedDataFilter.Count -ne 0) { $NamedDataFilter } else { }
            NamedDataExcludeFilter = if ($NamedDataExcludeFilter.Count -ne 0) { $NamedDataExcludeFilter } else { }
        }
    }
    if ($ExtendedInput.Count -gt 1) { $ExtendedInput } else { , $ExtendedInput }
}
function Move-ArchivedLogs {
    [CmdletBinding()]
    param ([string] $ServerName,
        [string] $SourcePath,
        [string] $DestinationPath)
    $NewSourcePath = "\\$ServerName\$($SourcePath.Replace(':\','$\'))"
    $PathExists = Test-Path -LiteralPath $NewSourcePath
    if ($PathExists) {
        Write-Color @script:WriteParameters '[i] Moving log file from ', $NewSourcePath, ' to ', $DestinationPath -Color White, Yellow, White, Yellow
        Move-Item -Path $NewSourcePath -Destination $DestinationPath
        if (!$?) { Write-Color @script:WriteParameters '[i] File ', $NewSourcePath, ' couldn not be moved.' -Color White, Yellow, White }
    } else { Write-Color @script:WriteParameters '[i] Event Log Move ', $NewSourcePath, ' was skipped. No file exists on drive.' -Color White, Yellow, White, Yellow }
}
function New-TargetServers {
    [CmdLetBinding()]
    param([string[]] $Servers,
        [switch] $UseDC)
    $Target = [ordered]@{Servers = [ordered] @{Enabled = if ($Servers -and ($UseDC -eq $false)) { $true } else { $false }
            Servers                                    = $Servers
        }
        DomainControllers        = [ordered] @{Enabled = $UseDC }
        LocalFiles               = [ordered] @{Enabled = $false
            Directories                                = [ordered] @{ }
            Files                                      = [ordered] @{ }
        }
    }
    return $Target
}
function Protect-ArchivedLogs {
    [CmdletBinding()]
    param ($TableEventLogClearedLogs,
        [string] $DestinationPath)
    foreach ($BackupEvent in $TableEventLogClearedLogs) {
        if ($BackupEvent.'Event ID' -eq 1105) {
            $SourcePath = $BackupEvent.'Backup Path'
            $ServerName = $BackupEvent.'Domain Controller'
            if ($SourcePath -and $ServerName -and $DestinationPath) {
                Write-Color @script:WriteParameters '[i] Found Event Log file ', $SourcePath, ' on ', $ServerName, '. Will try moving to: ', $DestinationPath -Color White, Yellow, White, Yellow
                Move-ArchivedLogs -ServerName $ServerName -SourcePath $SourcePath -DestinationPath $DestinationPath
            }
        }
    }
}
function Remove-ReportsFiles {
    [CmdletBinding()]
    param([bool] $KeepReports,
        [Array] $ReportFiles)
    if (-not $KeepReports) {
        foreach ($Report in $ReportFiles) {
            if ($Report -ne '' -and (Test-Path -LiteralPath $Report)) {
                $Logger.AddInfoRecord("Removing file $Report")
                try { Remove-Item -LiteralPath $Report -ErrorAction Stop } catch { $Logger.AddErrorRecord("Error removing file: $($_.Exception.Message)") }
            }
        }
    }
}
function Remove-Subscription {
    [CmdletBinding()]
    param([switch] $All,
        [switch] $Own,
        [System.Collections.IDictionary] $LoggerParameters)
    $Subscriptions = Start-MyProgram -Program $Script:ProgramWecutil -cmdArgList 'es'
    foreach ($Subscription in $Subscriptions) {
        if ($Own -eq $true -and $Subscription -like '*PSWinReporting*') {
            $Logger.AddInfoRecord("Deleting own providers - $Subscription")
            Start-MyProgram -Program $Script:ProgramWecutil -cmdArgList 'ds', $Subscription -LoggerParameters $LoggerParameters
        }
        if ($All -eq $true -and $Subscription -notlike '*PSWinReporting*') {
            $Logger.AddInfoRecord("Deleting own providers - $Subscription")
            Start-MyProgram -Program $Script:ProgramWecutil -cmdArgList 'ds', $Subscription -LoggerParameters $LoggerParameters
        }
    }
}
function Set-EmailReportDetails($FormattingParameters, $Dates, $Warnings) {
    $DateReport = Get-Date
    $Report = "<p style=`"background-color:white;font-family:$($FormattingParameters.FontFamily);font-size:$($FormattingParameters.FontSize)`">" +
    "<strong>Report Time:</strong> $DateReport <br>" +
    "<strong>Report Period:</strong> $($Dates.DateFrom) to $($Dates.DateTo) <br>" +
    "<strong>Account Executing Report :</strong> $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper()) <br>" +
    "<strong>Time to generate:</strong> **TimeToGenerateDays** days, **TimeToGenerateHours** hours, **TimeToGenerateMinutes** minutes, **TimeToGenerateSeconds** seconds, **TimeToGenerateMilliseconds** milliseconds"
    if ($($Warnings | Measure-Object).Count -gt 0) {
        $Report += "<br><br><strong>Warnings:</strong>"
        foreach ($warning in $Warnings) { $Report += "<br> $warning" }
    }
    $Report += "</p>"
    return $Report
}
function Set-ReportFile {
    param([string] $Path,
        [alias('FilePattern')][string] $FileNamePattern,
        [string] $DateFormat,
        [string] $Extension,
        [string] $ReportName)
    $FileNamePattern = $FileNamePattern.Replace('<currentdate>', $(Get-Date -f $DateFormat))
    $FileNamePattern = $FileNamePattern.Replace('<extension>', $Extension)
    $FileNamePattern = $FileNamePattern.Replace('<reportname>', $ReportName)
    return "$Path\$FileNamePattern"
}
function Set-ReportFileName {
    param([System.Collections.IDictionary] $ReportOptions,
        [string] $ReportExtension,
        [string] $ReportName = "")
    if ($ReportOptions.KeepReportsPath -ne "") { $Path = $ReportOptions.KeepReportsPath } else { $Path = $env:TEMP }
    $ReportPath = $Path + "\" + $ReportOptions.FilePattern
    $ReportPath = $ReportPath -replace "<currentdate>", $(Get-Date -f $ReportOptions.FilePatternDateFormat)
    if ($ReportName -ne "") { $ReportPath = $ReportPath.Replace(".<extension>", "-$ReportName.$ReportExtension") } else { $ReportPath = $ReportPath.Replace(".<extension>", ".$ReportExtension") }
    return $ReportPath
}
$Script:LoggerParameters = @{ShowTime = $false
    TimeFormat                        = 'yyyy-MM-dd HH:mm:ss'
}
$Script:ProgramWecutil = "wecutil.exe"
$Script:ProgramWevtutil = 'wevtutil.exe'
$Script:ReportDefinitions = [ordered] @{ADUserChanges = @{Enabled = $false
        SqlExport                                                 = @{EnabledGlobal = $false
            Enabled                                                                 = $false
            SqlServer                                                               = 'EVO1'
            SqlDatabase                                                             = 'SSAE18'
            SqlTable                                                                = 'dbo.[EventsNewSpecial]'
            SqlTableCreate                                                          = $true
            SqlTableAlterIfNeeded                                                   = $false
            SqlCheckBeforeInsert                                                    = 'EventRecordID', 'DomainController'
            SqlTableMapping                                                         = [ordered] @{'Event ID' = 'EventID,[int]'
                'Who'                                        = 'EventWho'
                'When'                                       = 'EventWhen,[datetime]'
                'Record ID'                                  = 'EventRecordID,[bigint]'
                'Domain Controller'                          = 'DomainController'
                'Action'                                     = 'Action'
                'Group Name'                                 = 'GroupName'
                'User Affected'                              = 'UserAffected'
                'Member Name'                                = 'MemberName'
                'Computer Lockout On'                        = 'ComputerLockoutOn'
                'Reported By'                                = 'ReportedBy'
                'SamAccountName'                             = 'SamAccountName'
                'Display Name'                               = 'DisplayName'
                'UserPrincipalName'                          = 'UserPrincipalName'
                'Home Directory'                             = 'HomeDirectory'
                'Home Path'                                  = 'HomePath'
                'Script Path'                                = 'ScriptPath'
                'Profile Path'                               = 'ProfilePath'
                'User Workstation'                           = 'UserWorkstation'
                'Password Last Set'                          = 'PasswordLastSet'
                'Account Expires'                            = 'AccountExpires'
                'Primary Group Id'                           = 'PrimaryGroupId'
                'Allowed To Delegate To'                     = 'AllowedToDelegateTo'
                'Old Uac Value'                              = 'OldUacValue'
                'New Uac Value'                              = 'NewUacValue'
                'User Account Control'                       = 'UserAccountControl'
                'User Parameters'                            = 'UserParameters'
                'Sid History'                                = 'SidHistory'
                'Logon Hours'                                = 'LogonHours'
                'OperationType'                              = 'OperationType'
                'Message'                                    = 'Message'
                'Backup Path'                                = 'BackupPath'
                'Log Type'                                   = 'LogType'
                'AddedWhen'                                  = 'EventAdded,[datetime],null'
                'AddedWho'                                   = 'EventAddedWho'
                'Gathered From'                              = 'GatheredFrom'
                'Gathered LogName'                           = 'GatheredLogName'
            }
        }
        Events                                                    = @{Enabled = $true
            Events                                                            = 4720, 4738
            LogName                                                           = 'Security'
            Fields                                                            = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'ObjectAffected'                    = 'User Affected'
                'SamAccountName'                    = 'SamAccountName'
                'DisplayName'                       = 'DisplayName'
                'UserPrincipalName'                 = 'UserPrincipalName'
                'HomeDirectory'                     = 'Home Directory'
                'HomePath'                          = 'Home Path'
                'ScriptPath'                        = 'Script Path'
                'ProfilePath'                       = 'Profile Path'
                'UserWorkstations'                  = 'User Workstations'
                'PasswordLastSet'                   = 'Password Last Set'
                'AccountExpires'                    = 'Account Expires'
                'PrimaryGroupId'                    = 'Primary Group Id'
                'AllowedToDelegateTo'               = 'Allowed To Delegate To'
                'OldUacValue'                       = 'Old Uac Value'
                'NewUacValue'                       = 'New Uac Value'
                'UserAccountControl'                = 'User Account Control'
                'UserParameters'                    = 'User Parameters'
                'SidHistory'                        = 'Sid History'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            Ignore                                                            = @{SubjectUserName = "ANONYMOUS LOGON" }
            Functions                                                         = @{'ProfilePath' = 'Convert-UAC'
                'OldUacValue'                = 'Remove-WhiteSpace', 'Convert-UAC'
                'NewUacValue'                = 'Remove-WhiteSpace', 'Convert-UAC'
                'UserAccountControl'         = 'Remove-WhiteSpace', 'Split-OnSpace', 'Convert-UAC'
            }
            IgnoreWords                                                       = @{ }
            SortBy                                                            = 'When'
        }
    }
    ADUserChangesDetailed                             = [ordered] @{Enabled = $false
        Events                                                              = @{Enabled = $true
            Events                                          = 5136, 5137, 5139, 5141
            LogName                                         = 'Security'
            Filter                                          = [ordered] @{'ObjectClass' = 'user' }
            Functions                                       = @{'OperationType' = 'ConvertFrom-OperationType' }
            Fields                                          = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'OperationType'                     = 'Action Detail'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ObjectDN'                          = 'User Object'
                'AttributeLDAPDisplayName'          = 'Field Changed'
                'AttributeValue'                    = 'Field Value'
                'RecordID'                          = 'Record ID'
                'ID'                                = 'Event ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                          = 'Record ID'
            Descending                                      = $false
            IgnoreWords                                     = @{ }
        }
    }
    ADComputerChangesDetailed                         = [ordered] @{Enabled = $false
        Events                                                              = @{Enabled = $true
            Events                                              = 5136, 5137, 5139, 5141
            LogName                                             = 'Security'
            Filter                                              = @{'ObjectClass' = 'computer' }
            Functions                                           = @{'OperationType' = 'ConvertFrom-OperationType' }
            Fields                                              = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'OperationType'                     = 'Action Detail'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ObjectDN'                          = 'Computer Object'
                'AttributeLDAPDisplayName'          = 'Field Changed'
                'AttributeValue'                    = 'Field Value'
                'RecordID'                          = 'Record ID'
                'ID'                                = 'Event ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                              = 'Record ID'
            Descending                                          = $false
            IgnoreWords                                         = @{ }
        }
    }
    ADOrganizationalUnitChangesDetailed               = [ordered] @{Enabled = $false
        OUEventsModify                                                      = @{Enabled = $true
            Events                                                        = 5136, 5137, 5139, 5141
            LogName                                                       = 'Security'
            Filter                                                        = [ordered] @{'ObjectClass' = 'organizationalUnit' }
            Functions                                                     = @{'OperationType' = 'ConvertFrom-OperationType' }
            Fields                                                        = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                                    = 'Action'
                'OperationType'                             = 'Action Detail'
                'Who'                                       = 'Who'
                'Date'                                      = 'When'
                'ObjectDN'                                  = 'Organizational Unit'
                'AttributeLDAPDisplayName'                  = 'Field Changed'
                'AttributeValue'                            = 'Field Value'
                'RecordID'                                  = 'Record ID'
                'ID'                                        = 'Event ID'
                'GatheredFrom'                              = 'Gathered From'
                'GatheredLogName'                           = 'Gathered LogName'
            }
            Overwrite                                                     = [ordered] @{'Action Detail#1' = 'Action', 'A directory service object was created.', 'Organizational Unit Created'
                'Action Detail#2'                                  = 'Action', 'A directory service object was deleted.', 'Organizational Unit Deleted'
                'Action Detail#3'                                  = 'Action', 'A directory service object was moved.', 'Organizational Unit Moved'
            }
            OverwriteByField                                              = [ordered] @{'Organizational Unit' = 'Action', 'A directory service object was moved.', 'OldObjectDN'
                'Field Value'                                          = 'Action', 'A directory service object was moved.', 'NewObjectDN'
            }
            SortBy                                                        = 'Record ID'
            Descending                                                    = $false
            IgnoreWords                                                   = @{ }
        }
    }
    ADUserStatus                                      = [ordered] @{Enabled = $false
        Events                                                              = @{Enabled = $true
            Events                                 = 4722, 4725, 4767, 4723, 4724, 4726
            LogName                                = 'Security'
            IgnoreWords                            = @{ }
            Fields                                 = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ObjectAffected'                    = 'User Affected'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                 = 'When'
        }
    }
    ADUserLockouts                                    = [ordered] @{Enabled = $false
        Events                                                              = @{Enabled = $true
            Events                                   = 4740
            LogName                                  = 'Security'
            IgnoreWords                              = @{ }
            Fields                                   = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'TargetDomainName'                  = 'Computer Lockout On'
                'ObjectAffected'                    = 'User Affected'
                'Who'                               = 'Reported By'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                   = 'When'
        }
    }
    ADUserLogon                                       = [ordered]@{Enabled = $false
        Events                                                             = @{Enabled = $true
            Events                               = 4624
            LogName                              = 'Security'
            Fields                               = [ordered] @{'Computer' = 'Computer'
                'Action'                            = 'Action'
                'IpAddress'                         = 'IpAddress'
                'IpPort'                            = 'IpPort'
                'ObjectAffected'                    = 'User / Computer Affected'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'LogonProcessName'                  = 'LogonProcessName'
                'ImpersonationLevel'                = 'ImpersonationLevel'
                'VirtualAccount'                    = 'VirtualAccount'
                'ElevatedToken'                     = 'ElevatedToken'
                'LogonType'                         = 'LogonType'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            IgnoreWords                          = @{ }
        }
    }
    ADUserUnlocked                                    = [ordered] @{Enabled = $false
        Events                                                              = @{Enabled = $true
            Events                                   = 4767
            LogName                                  = 'Security'
            IgnoreWords                              = @{ }
            Functions                                = @{ }
            Fields                                   = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'TargetDomainName'                  = 'Computer Lockout On'
                'ObjectAffected'                    = 'User Affected'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                   = 'When'
        }
    }
    ADComputerCreatedChanged                          = [ordered] @{Enabled = $false
        Events                                                              = @{Enabled = $true
            Events                                             = 4741, 4742
            LogName                                            = 'Security'
            Ignore                                             = @{SubjectUserName = "ANONYMOUS LOGON" }
            Fields                                             = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'ObjectAffected'                    = 'Computer Affected'
                'SamAccountName'                    = 'SamAccountName'
                'DisplayName'                       = 'DisplayName'
                'UserPrincipalName'                 = 'UserPrincipalName'
                'HomeDirectory'                     = 'Home Directory'
                'HomePath'                          = 'Home Path'
                'ScriptPath'                        = 'Script Path'
                'ProfilePath'                       = 'Profile Path'
                'UserWorkstations'                  = 'User Workstations'
                'PasswordLastSet'                   = 'Password Last Set'
                'AccountExpires'                    = 'Account Expires'
                'PrimaryGroupId'                    = 'Primary Group Id'
                'AllowedToDelegateTo'               = 'Allowed To Delegate To'
                'OldUacValue'                       = 'Old Uac Value'
                'NewUacValue'                       = 'New Uac Value'
                'UserAccountControl'                = 'User Account Control'
                'UserParameters'                    = 'User Parameters'
                'SidHistory'                        = 'Sid History'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            IgnoreWords                                        = @{ }
        }
    }
    ADComputerDeleted                                 = [ordered]@{Enabled = $false
        Events                                                             = @{Enabled = $true
            Events                                     = 4743
            LogName                                    = 'Security'
            IgnoreWords                                = @{ }
            Fields                                     = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'ObjectAffected'                    = 'Computer Affected'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                     = 'When'
        }
    }
    ADUserLogonKerberos                               = [ordered] @{Enabled = $false
        Events                                                              = @{Enabled = $true
            Events                                        = 4768
            LogName                                       = 'Security'
            IgnoreWords                                   = @{ }
            Functions                                     = [ordered] @{'IpAddress' = 'Clean-IpAddress' }
            Fields                                        = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'ObjectAffected'                    = 'Computer/User Affected'
                'IpAddress'                         = 'IpAddress'
                'IpPort'                            = 'Port'
                'TicketOptions'                     = 'TicketOptions'
                'Status'                            = 'Status'
                'TicketEncryptionType'              = 'TicketEncryptionType'
                'PreAuthType'                       = 'PreAuthType'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                        = 'When'
        }
    }
    ADGroupMembershipChanges                          = [ordered]@{Enabled = $false
        Events                                                             = @{Enabled = $true
            Events                                            = 4728, 4729, 4732, 4733, 4746, 4747, 4751, 4752, 4756, 4757, 4761, 4762, 4785, 4786, 4787, 4788
            LogName                                           = 'Security'
            IgnoreWords                                       = @{ }
            Fields                                            = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'TargetUserName'                    = 'Group Name'
                'MemberNameWithoutCN'               = 'Member Name'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                            = 'When'
        }
    }
    ADGroupEnumeration                                = [ordered] @{Enabled = $false
        Events                                                              = @{Enabled = $true
            Events                                       = 4798, 4799
            LogName                                      = 'Security'
            IgnoreWords                                  = [ordered] @{ }
            Fields                                       = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'TargetUserName'                    = 'Group Name'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                       = 'When'
        }
    }
    ADGroupChanges                                    = [ordered]@{Enabled = $false
        Events                                                             = @{Enabled = $true
            Events                                  = 4735, 4737, 4745, 4750, 4760, 4764, 4784, 4791
            LogName                                 = 'Security'
            IgnoreWords                             = @{'Who' = '*ANONYMOUS*' }
            Fields                                  = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'TargetUserName'                    = 'Group Name'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'GroupTypeChange'                   = 'Changed Group Type'
                'SamAccountName'                    = 'Changed SamAccountName'
                'SidHistory'                        = 'Changed SidHistory'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                  = 'When'
        }
    }
    ADGroupCreateDelete                               = [ordered]@{Enabled = $false
        Events                                                             = @{Enabled = $true
            Events                                       = 4727, 4730, 4731, 4734, 4744, 4748, 4749, 4753, 4754, 4758, 4759, 4763
            LogName                                      = 'Security'
            IgnoreWords                                  = @{ }
            Fields                                       = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'TargetUserName'                    = 'Group Name'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                       = 'When'
        }
    }
    ADGroupChangesDetailed                            = [ordered] @{Enabled = $false
        Events                                                              = @{Enabled = $true
            Events                                           = 5136, 5137, 5141
            LogName                                          = 'Security'
            Filter                                           = [ordered] @{'ObjectClass' = 'group' }
            Functions                                        = @{'OperationType' = 'ConvertFrom-OperationType' }
            Fields                                           = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'OperationType'                     = 'Action Detail'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ObjectDN'                          = 'Computer Object'
                'ObjectClass'                       = 'ObjectClass'
                'AttributeLDAPDisplayName'          = 'Field Changed'
                'AttributeValue'                    = 'Field Value'
                'RecordID'                          = 'Record ID'
                'ID'                                = 'Event ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                           = 'Record ID'
            Descending                                       = $false
            IgnoreWords                                      = @{ }
        }
    }
    ADGroupPolicyChanges                              = [ordered] @{Enabled = $false
        'Group Policy Name Changes'                                         = @{Enabled = $true
            Events                                         = 5136, 5137, 5141
            LogName                                        = 'Security'
            Filter                                         = [ordered] @{'ObjectClass' = 'groupPolicyContainer'
                'AttributeLDAPDisplayName'                                  = $null, 'displayName'
            }
            Functions                                      = @{'OperationType' = 'ConvertFrom-OperationType' }
            Fields                                         = [ordered] @{'RecordID' = 'Record ID'
                'Computer'                                               = 'Domain Controller'
                'Action'                                                 = 'Action'
                'Who'                                                    = 'Who'
                'Date'                                                   = 'When'
                'ObjectDN'                                               = 'ObjectDN'
                'ObjectGUID'                                             = 'ObjectGUID'
                'ObjectClass'                                            = 'ObjectClass'
                'AttributeLDAPDisplayName'                               = 'AttributeLDAPDisplayName'
                'AttributeValue'                                         = 'AttributeValue'
                'OperationType'                                          = 'OperationType'
                'OpCorrelationID'                                        = 'OperationCorelationID'
                'AppCorrelationID'                                       = 'OperationApplicationCorrelationID'
                'DSName'                                                 = 'DSName'
                'DSType'                                                 = 'DSType'
                'Task'                                                   = 'Task'
                'Version'                                                = 'Version'
                'ID'                                                     = 'Event ID'
                'GatheredFrom'                                           = 'Gathered From'
                'GatheredLogName'                                        = 'Gathered LogName'
            }
            SortBy                                         = 'Record ID'
            Descending                                     = $false
            IgnoreWords                                    = @{ }
        }
        'Group Policy Edits'                                                = @{Enabled = $true
            Events                                         = 5136, 5137, 5141
            LogName                                        = 'Security'
            Filter                                         = [ordered] @{'ObjectClass' = 'groupPolicyContainer'
                'AttributeLDAPDisplayName'                           = 'versionNumber'
            }
            Functions                                      = @{'OperationType' = 'ConvertFrom-OperationType' }
            Fields                                         = [ordered] @{'RecordID' = 'Record ID'
                'Computer'                                        = 'Domain Controller'
                'Action'                                          = 'Action'
                'Who'                                             = 'Who'
                'Date'                                            = 'When'
                'ObjectDN'                                        = 'ObjectDN'
                'ObjectGUID'                                      = 'ObjectGUID'
                'ObjectClass'                                     = 'ObjectClass'
                'AttributeLDAPDisplayName'                        = 'AttributeLDAPDisplayName'
                'AttributeValue'                                  = 'AttributeValue'
                'OperationType'                                   = 'OperationType'
                'OpCorrelationID'                                 = 'OperationCorelationID'
                'AppCorrelationID'                                = 'OperationApplicationCorrelationID'
                'DSName'                                          = 'DSName'
                'DSType'                                          = 'DSType'
                'Task'                                            = 'Task'
                'Version'                                         = 'Version'
                'ID'                                              = 'Event ID'
                'GatheredFrom'                                    = 'Gathered From'
                'GatheredLogName'                                 = 'Gathered LogName'
            }
            SortBy                                         = 'Record ID'
            Descending                                     = $false
            IgnoreWords                                    = @{ }
        }
        'Group Policy Links'                                                = @{Enabled = $true
            Events                                         = 5136, 5137, 5141
            LogName                                        = 'Security'
            Filter                                         = @{'ObjectClass' = 'domainDNS' }
            Functions                                      = @{'OperationType' = 'ConvertFrom-OperationType' }
            Fields                                         = [ordered] @{'RecordID' = 'Record ID'
                'Computer'                                        = 'Domain Controller'
                'Action'                                          = 'Action'
                'Who'                                             = 'Who'
                'Date'                                            = 'When'
                'ObjectDN'                                        = 'ObjectDN'
                'ObjectGUID'                                      = 'ObjectGUID'
                'ObjectClass'                                     = 'ObjectClass'
                'AttributeLDAPDisplayName'                        = 'AttributeLDAPDisplayName'
                'AttributeValue'                                  = 'AttributeValue'
                'OperationType'                                   = 'OperationType'
                'OpCorrelationID'                                 = 'OperationCorelationID'
                'AppCorrelationID'                                = 'OperationApplicationCorrelationID'
                'DSName'                                          = 'DSName'
                'DSType'                                          = 'DSType'
                'Task'                                            = 'Task'
                'Version'                                         = 'Version'
                'ID'                                              = 'Event ID'
                'GatheredFrom'                                    = 'Gathered From'
                'GatheredLogName'                                 = 'Gathered LogName'
            }
            SortBy                                         = 'Record ID'
            Descending                                     = $false
            IgnoreWords                                    = @{ }
        }
    }
    ADLogsClearedSecurity                             = [ordered]@{Enabled = $false
        Events                                                             = @{Enabled = $true
            Events                                         = 1102, 1105
            LogName                                        = 'Security'
            Fields                                         = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'BackupPath'                        = 'Backup Path'
                'Channel'                           = 'Log Type'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
                'GatheredLogName'                   = 'Gathered LogName'
            }
            SortBy                                         = 'When'
            IgnoreWords                                    = @{ }
            Overwrite                                      = [ordered] @{'Backup Path' = 'Backup Path', '', 'N/A'
                'Who'                                  = 'Event ID', 1105, 'Automatic Backup'
            }
        }
    }
    ADLogsClearedOther                                = [ordered]@{Enabled = $false
        Events                                                             = @{Enabled = $true
            Events                                      = 104
            LogName                                     = 'System'
            IgnoreWords                                 = @{ }
            Fields                                      = [ordered] @{'Computer' = 'Domain Controller'
                'Action'                            = 'Action'
                'BackupPath'                        = 'Backup Path'
                'Channel'                           = 'Log Type'
                'Who'                               = 'Who'
                'Date'                              = 'When'
                'ID'                                = 'Event ID'
                'RecordID'                          = 'Record ID'
                'GatheredFrom'                      = 'Gathered From'
            }
            SortBy                                      = 'When'
            Overwrite                                   = @{'Backup Path' = 'Backup Path', '', 'N/A' }
        }
    }
    NetworkAccessAuthenticationPolicy                 = [ordered]@{Enabled = $false
        Events                                                             = @{Enabled = $true
            Events                                                     = 6272, 6273
            LogName                                                    = 'Security'
            IgnoreWords                                                = @{ }
            Fields                                                     = [ordered] @{'Action'       = 'Action'
                'SubjectUserSid'                = 'SecurityID'
                'Computer'                      = 'Compuer'
                'SubjectUserName'               = 'AccountName'
                'SubjectDomainName'             = 'Account Domain'
                'CalledStationID'               = 'CalledStationID'
                'CallingStationID'              = 'CallingStationID'
                'NASIPv4Address'                = 'NASIPv4Address'
                'NASIPv6Address'                = 'NASIPv6Address'
                'NASIdentifier'                 = 'NASIdentifier'
                'NASPortType'                   = 'NASPortType'
                'NASPort'                       = 'NASPort'
                'ClientName'                    = 'ClientFriendlyName'
                'ClientIPAddress'               = 'ClientFriendlyIPAddress'
                'ProxyPolicyName'               = 'ConnectionRequestPolicyName'
                'NetworkPolicyName'             = 'NetworkPolicyName'
                'AuthenticationProvider'        = 'AuthenticationProvider'
                'AuthenticationServer'          = 'AuthenticationServer'
                'AuthenticationType'            = 'AuthenticationType'
                'EAPType'                       = 'EAPType'
                'Reason'                        = 'Reason'
                'ReasonCode'                    = 'ReasonCode'
                'FullyQualifiedSubjectUserName' = 'Who'
                'Date'                          = 'When'
                'ID'                            = 'Event ID'
                'RecordID'                      = 'Record ID'
                'GatheredFrom'                  = 'Gathered From'
                'GatheredLogName'               = 'Gathered LogName'
            }
            SortBy                                                     = 'When'
        }
    }
    "OSCrash"                                         = [ordered]@{Enabled = $false
        Events                                                             = @{Enabled = $true
            Events                             = 6008
            LogName                            = 'System'
            IgnoreWords                        = @{ }
            Fields                             = [ordered] @{"Computer" = "Computer"
                'Date'                              = 'When'
                "MachineName"                       = "ObjectAffected"
                "EventAction"                       = "Action"
                "Message"                           = "ActionDetails"
                "NoNameA1"                          = "ActionDetailsDate"
                "NoNameA0"                          = "ActionDetailsTime"
                "ID"                                = "Event ID"
                "RecordID"                          = "Record ID"
                "GatheredFrom"                      = "Gathered From"
                "GatheredLogName"                   = "Gathered LogName"
            }
            Overwrite                          = @{"Action#1" = "Event ID" , 6008, "System Crash" }
        }
    }
    "OSStartupShutdownCrash"                          = [ordered]@{Enabled = $false
        Events                                                             = [ordered] @{Enabled = $true
            Events                                                      = 12, 13, 41, 4608, 4621, 6008
            LogName                                                     = 'System'
            IgnoreWords                                                 = @{ }
            Filter                                                      = [ordered] @{'ProviderName' = 'Microsoft-Windows-Kernel-General', 'EventLog' }
            FilterOr                                                    = [ordered] @{ }
            Fields                                                      = [ordered] @{"Computer" = "Computer"
                'Date'                                        = 'When'
                "MachineName"                                 = "ObjectAffected"
                "EventAction"                                 = "Action"
                "Message"                                     = "ActionDetails"
                "NoNameA1"                                    = "ActionDetailsDate"
                "NoNameA0"                                    = "ActionDetailsTime"
                "ActionDetailsDateTime"                       = "ActionDetailsDateTime"
                "ID"                                          = "Event ID"
                "RecordID"                                    = "Record ID"
                "GatheredFrom"                                = "Gathered From"
                "GatheredLogName"                             = "Gathered LogName"
            }
            Overwrite                                                   = [ordered] @{"Action#1" = "Event ID", 12, "System Start"
                "Action#2"                                    = "Event ID", 13, "System Shutdown"
                "Action#3"                                    = "Event ID", 41, "System Dirty Reboot"
                "Action#4"                                    = "Event ID", 4608, "Windows is starting up"
                "Action#5"                                    = "Event ID", 4621, "Administrator recovered system from CrashOnAuditFail"
                "Action#6"                                    = "Event ID", 6008, "System Crash"
            }
            OverwriteByField                                            = @{'ActionDetailsDateTime#1#ne' = 'StartTime', $null, 'StartTime'
                'ActionDetailsDateTime#2#ne'                          = '#text', $null, '#text'
            }
        }
    }
}
$Script:ReportTimes = [ordered] @{PastHour = @{Enabled = $false }
    CurrentHour                            = @{Enabled = $false }
    PastDay                                = @{Enabled = $false }
    CurrentDay                             = @{Enabled = $false }
    OnDay                                  = @{Enabled = $false
        Days                                           = 'Monday'
    }
    PastMonth                              = @{Enabled = $false
        Force                                          = $true
    }
    CurrentMonth                           = @{Enabled = $false }
    PastQuarter                            = @{Enabled = $false
        Force                                          = $true
    }
    CurrentQuarter                         = @{Enabled = $false }
    CurrentDayMinusDayX                    = @{Enabled = $false
        Days                                           = 7
    }
    CurrentDayMinuxDaysX                   = @{Enabled = $false
        Days                                           = 3
    }
    CustomDate                             = @{Enabled = $false
        DateFrom                                       = Get-Date -Year 2018 -Month 03 -Day 19
        DateTo                                         = Get-Date -Year 2018 -Month 03 -Day 23
    }
    Last3days                              = @{Enabled = $false }
    Last7days                              = @{Enabled = $false }
    Last14days                             = @{Enabled = $false }
    Everything                             = @{Enabled = $false }
}
function Add-ServersToXML {
    [CmdletBinding()]
    param ([string] $FilePath,
        [string[]] $Servers)
    [xml]$xmlDocument = Get-Content -Path $FilePath -Encoding UTF8
    foreach ($Server in $Servers) {
        $node = $xmlDocument.CreateElement('EventSource', $xmlDocument.Subscription.NamespaceURI)
        $node.SetAttribute('Enabled', 'true')
        $nodeServer = $xmlDocument.CreateElement('Address', $xmlDocument.Subscription.NamespaceURI)
        $nodeServer.set_InnerXML($Server)
        [void] $xmlDocument.Subscription.Eventsources.AppendChild($node)
        [void] $xmlDocument.Subscription.Eventsources.EventSource.AppendChild($nodeServer)
    }
    Save-XML -FilePath $FilePath -xml $xmlDocument
}
function Set-SubscriptionTemplates {
    [CmdletBinding()]
    param([System.Array] $ListTemplates,
        [switch] $DeleteOwn,
        [switch] $DeleteAllOther,
        [System.Collections.IDictionary] $LoggerParameters)
    if (-not $LoggerParameters) { $LoggerParameters = $Script:LoggerParameters }
    $Logger = Get-Logger @LoggerParameters
    if ($DeleteAll -or $DeleteOwn) { Remove-Subscription -All:$DeleteAllOther -Own:$DeleteOwn -LoggerParameters $LoggerParameters }
    foreach ($TemplatePath in $ListTemplates) {
        $Logger.AddInfoRecord("Adding provider $TemplatePath to Subscriptions.")
        Start-MyProgram -Program $Script:ProgramWecutil -cmdArgList 'cs', $TemplatePath -LoggerParameters $LoggerParameters
    }
}
function Add-EventsDefinitions {
    [CmdLetBinding()]
    param([parameter(Mandatory = $true)][System.Collections.IDictionary] $Definitions,
        [parameter(Mandatory = $true)][string] $Name,
        [switch] $Force)
    $AllDefinitions = Get-EventsDefintions -Definitions $Definitions
    if ($null -ne $AllDefinitions) {
        [string] $ConfigurationPath = "$Env:ALLUSERSPROFILE\Evotec\PSWinReporting\Definitions"
        $null = New-Item -Type Directory -Path $ConfigurationPath -Force
        if (Test-Path -LiteralPath $ConfigurationPath) {
            $XMLPath = "$ConfigurationPath\$Name.xml"
            if ((Test-Path -LiteralPath $XMLPath) -and (-not $Force)) {
                Write-Warning -Message "Definition with name $Name already exists. Please choose another name or use -Force switch."
                return
            }
            $Definitions | Export-Clixml -LiteralPath $XMLPath -Depth 5
        }
    }
}
function Add-WinTaskScheduledForwarder {
    [CmdletBinding()]
    param([string] $TaskPath = '\Event Viewer Tasks\',
        [string] $TaskName = 'ForwardedEvents',
        [string] $Author = 'Evotec',
        [string] $URI = '\Event Viewer Tasks\ForwardedEvents',
        [string] $Command = 'powershell.exe',
        [Array] $Argument = @('-windowstyle hidden', 'C:\Support\GitHub\PSWinReporting\Examples\Trigger.ps1', "-EventID $(eventID) -eventRecordID '$(eventRecordID)' -eventChannel '$(eventChannel)' -eventSeverity $(eventSeverity)"),
        [System.Collections.IDictionary] $LoggerParameters)
    if (-not $LoggerParameters) { $LoggerParameters = $Script:LoggerParameters }
    $Logger = Get-Logger @LoggerParameters
    $xmlTemplate = "$($($(Get-Module -ListAvailable PSWinReportingV2)[0]).ModuleBase)\Templates\Template-ScheduledTask.xml"
    if (Test-Path -LiteralPath $xmlTemplate) {
        $Logger.AddInfoRecord("Found Template $xmlTemplate")
        $ScheduledTaskXML = "$ENV:TEMP\PSWinReportingSchedluledTask.xml"
        Copy-Item -Path $xmlTemplate -Destination $ScheduledTaskXML
        $Logger.AddInfoRecord("Copied template $ScheduledTaskXML")
        Set-XML -FilePath $ScheduledTaskXML -Paths 'Task', 'RegistrationInfo' -Node 'Author' -Value $Author
        Set-XML -FilePath $ScheduledTaskXML -Paths 'Task', 'Actions', 'Exec' -Node 'Command' -Value $Command
        Set-XML -FilePath $ScheduledTaskXML -Paths 'Task', 'Actions', 'Exec' -Node 'Arguments' -Value ([string] $Argument)
        $xml = (Get-Content -LiteralPath $ScheduledTaskXML | Out-String)
        try { $Output = Register-ScheduledTask -TaskPath $TaskPath -TaskName $TaskName -xml $xml -ErrorAction Stop } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            switch ($ErrorMessage) { default { $Logger.AddErrorRecord("Tasks adding error occured: $ErrorMessage") } }
            Exit
        }
        $Logger.AddInfoRecord("Loaded template $ScheduledTaskXML")
    } else { $Logger.AddErrorRecord("Template not found $xmlTemplate") }
}
function Find-Events {
    [CmdLetBinding(DefaultParameterSetName = 'Manual')]
    param([parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "Manual", Mandatory = $true)][DateTime] $DateFrom,
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "Manual", Mandatory = $true)][DateTime] $DateTo,
        [parameter(ParameterSetName = "Manual")]
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "DateRange", Mandatory = $false)][alias('Server', 'ComputerName')][string[]] $Servers = $Env:COMPUTERNAME,
        [parameter(ParameterSetName = "Manual")]
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "DateRange", Mandatory = $false)][alias('RunAgainstDC')][switch] $DetectDC,
        [ValidateNotNull()]
        [alias('Credentials')][System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
        [parameter(ParameterSetName = "Manual")]
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "DateRange")][switch] $Quiet,
        [parameter(ParameterSetName = "Manual")]
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "DateRange")][System.Collections.IDictionary] $LoggerParameters,
        [parameter(ParameterSetName = "Manual")]
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "DateRange")][switch] $ExtentedOutput,
        [parameter(ParameterSetName = "Manual")]
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "DateRange")][string] $Who,
        [parameter(ParameterSetName = "Manual")]
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "DateRange")][string] $Whom,
        [parameter(ParameterSetName = "Manual")]
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "DateRange")][string] $NotWho,
        [parameter(ParameterSetName = "Manual")]
        [parameter(ParameterSetName = "DateManual")]
        [parameter(ParameterSetName = "DateRange")][string] $NotWhom,
        [parameter(ParameterSetName = "Extended", Mandatory = $true)][System.Collections.IDictionary] $Definitions,
        [parameter(ParameterSetName = "Extended", Mandatory = $true)][System.Collections.IDictionary] $Times,
        [parameter(ParameterSetName = "Extended", Mandatory = $true)][System.Collections.IDictionary] $Target,
        [parameter(ParameterSetName = "Extended", Mandatory = $false)][int] $EventID,
        [parameter(ParameterSetName = "Extended", Mandatory = $false)][int64] $EventRecordID)
    DynamicParam {
        $ParameterSetsAttributesDateManual = New-Object System.Management.Automation.ParameterAttribute
        $ParameterSetsAttributesDateManual.Mandatory = $true
        $ParameterSetsAttributesDateManual.ParameterSetName = 'DateManual'
        $ParamAttribDatesRange = New-Object System.Management.Automation.ParameterAttribute
        $ParamAttribDatesRange.Mandatory = $true
        $ParamAttribDatesRange.ParameterSetName = 'DateRange'
        $ParameterSetsAttributes = New-Object System.Management.Automation.ParameterAttribute
        $ParameterSetsAttributes.Mandatory = $true
        $ParameterSetsAttributes.ParameterSetName = 'Manual'
        $Names = (Get-EventsDefinitions).Keys
        $ReportAttrib = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $ReportAttrib.Add($ParameterSetsAttributes)
        $ReportAttrib.Add($ParamAttribDatesRange)
        $ReportAttrib.Add($ParameterSetsAttributesDateManual)
        $ReportAttrib.Add((New-Object System.Management.Automation.ValidateSetAttribute($Names)))
        $ReportRuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Report', [string[]], $ReportAttrib)
        $DatesRange = (Get-DatesDefinitions -Skip 'CustomDate', 'CurrentDayMinuxDaysX', 'CurrentDayMinusDayX', 'OnDay')
        $DatesRangeAttrib = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $DatesRangeAttrib.Add($ParamAttribDatesRange)
        $DatesRangeAttrib.Add((New-Object System.Management.Automation.ValidateSetAttribute($DatesRange)))
        $DatesRangeRuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter('DatesRange', [string], $DatesRangeAttrib)
        $RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $RuntimeParamDic.Add('Report', $ReportRuntimeParam)
        $RuntimeParamDic.Add('DatesRange', $DatesRangeRuntimeParam)
        return $RuntimeParamDic
    }
    Process {
        $ExecutionTime = Start-TimeLog
        if (-not $LoggerParameters) { $LoggerParameters = $Script:LoggerParameters }
        $Logger = Get-Logger @LoggerParameters
        if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) { $Verbose = $true } else { $Verbose = $false }
        if ($null -ne $Definitions -and $null -ne $Times -and $null -ne $Target) {
            $Dates = Get-ChoosenDates -ReportTimes $Times
            if (-not $Dates) {
                $Logger.AddErrorRecord("Not a single date was choosen for scan. Please fix Times and try again.")
                return
            }
            if ($Dates -is [Array]) {
                $Logger.AddErrorRecord("Currently only 1 date range is supported. Please fix Times and try again.")
                return
            }
            $Reports = foreach ($Report in $Definitions.Keys) { if ($Definitions[$Report].Enabled -eq $true) { $Report } }
        } else {
            $Reports = $PSBoundParameters.Report
            $DatesRange = $PSBoundParameters.DatesRange
            if (-not $Quiet) { $Logger.AddInfoRecord("Preparing reports: $($Reports -join ',')") }
            $Definitions = $Script:ReportDefinitions
            $Times = $Script:ReportTimes
            if ($DatesRange) { $Times.$DatesRange.Enabled = $true } elseif ($DateFrom -and $DateTo) {
                $Times.CustomDate.Enabled = $true
                $Times.CustomDate.DateFrom = $DateFrom
                $Times.CustomDate.DateTo = $DateTo
            } else { return }
            $Dates = Get-ChoosenDates -ReportTimes $Times
            if (-not $Dates) {
                $Logger.AddErrorRecord("Not a single date was choosen for scan. Please fix Times and try again.")
                return
            }
            if ($Dates -is [Array]) {
                $Logger.AddErrorRecord("Currently only 1 date range is supported. Please fix Times and try again")
                return
            }
            foreach ($Report in $Reports) { $Definitions[$Report].Enabled = $true }
            $Target = New-TargetServers -Servers $Servers -UseDC:$DetectDC
        }
        if ($EventRecordID -ne 0 -and $EventID -ne 0) { [Array] $ExtendedInput = Get-ServersListLimited -Target $Target -RecordID $EventRecordID -Quiet:$Quiet -Who $Who -Whom $Whom -NotWho $NotWho -NotWhom $NotWhom } else { [Array] $ExtendedInput = Get-ServersList -Definitions $Definitions -Target $Target -Dates $Dates -Quiet:$Quiet -Who $Who -Whom $Whom -NotWho $NotWho -NotWhom $NotWhom }
        if (-not $ExtendedInput) {
            $Logger.AddErrorRecord("There are no logs/servers to scan. Please fix Targets and try again.")
            return
        }
        foreach ($Entry in $ExtendedInput) { if ($Entry.Type -eq 'Computer') { if (-not $Quiet) { $Logger.AddInfoRecord("Computer $($Entry.Server) added to scan $($Entry.LogName) log for events: $($Entry.EventID -join ', ')") } } else { if (-not $Quiet) { $Logger.AddInfoRecord("File $($Entry.Server) added to scan $($Entry.LogName) log for events: $($Entry.EventID -join ', ')") } } }
        if (-not $Quiet) { $Logger.AddInfoRecord("Getting events for dates $($Dates.DateFrom) to $($Dates.DateTo)") }
        $SplatEvents = @{Verbose = $Verbose
            ExtendedInput        = $ExtendedInput
            ErrorVariable        = 'AllErrors'
            ErrorAction          = 'SilentlyContinue'
        }
        if ($EventRecordID -ne 0 -and $EventId -ne 0) {
            $SplatEvents.RecordID = $EventRecordID
            $SplatEvents.ID = $EventID
        }
        if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $SplatEvents.Credential = $Credential }
        [Array] $AllEvents = Get-Events @SplatEvents
        foreach ($MyError in $AllErrors) { if (-not $Quiet) { $Logger.AddErrorRecord("Server $MyError") } }
        $Elapsed = Stop-TimeLog -Time $ExecutionTime -Option OneLiner
        if (-not $Quiet) { $Logger.AddInfoRecord("Events scanned found $($AllEvents.Count) - Time elapsed: $Elapsed") }
        $Results = Get-EventsOutput -Definitions $Definitions -AllEvents $AllEvents -Quiet:$Quiet
        if ($Results.Count -eq 1) { $Results[$Reports] } else { $Results }
        foreach ($Report in $Script:ReportDefinitions.Keys) { $Script:ReportDefinitions[$Report].Enabled = $false }
        foreach ($Time in $Script:ReportTimes.Keys) { $Script:ReportTimes[$Time].Enabled = $false }
    }
}
function New-WinSubscriptionTemplates {
    [CmdletBinding()]
    param ([string[]] $Servers,
        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [string[]] $ExcludeDomainControllers,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [alias('DomainControllers')][string[]] $IncludeDomainControllers,
        [switch] $SkipRODC,
        [ValidateScript( { $_ -in (& $SourcesAutoCompleter) })][string[]] $Reports,
        [switch] $AddTemplates,
        [alias('ReportDefinitions')][System.Collections.IDictionary] $Definitions,
        [System.Collections.IDictionary] $Target,
        [System.Collections.IDictionary] $LoggerParameters)
    Begin {
        if (-not $LoggerParameters) { $LoggerParameters = $Script:LoggerParameters }
        $Logger = Get-Logger @LoggerParameters
        if (-not $Reports) { $Reports = (Get-EventsDefinitions).Keys }
        if (-not $Definitions) { $Definitions = $Script:ReportDefinitions }
        foreach ($Report in $Reports) { $Definitions[$Report].Enabled = $true }
        if (-not $Target) {
            $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExcludeDomainControllers $ExcludeDomainControllers -IncludeDomainControllers $IncludeDomainControllers -SkipRODC:$SkipRODC
            $Target = [ordered]@{Servers = [ordered] @{Enabled = $true
                    ServerDCs                                  = $ForestInformation.ForestDomainControllers.HostName
                    ServerOther                                = $Servers
                }
            }
        }
    }
    Process {
        [Array] $ExtendedInput = Get-ServersList -Definitions $Definitions -Target $Target
        foreach ($Entry in $ExtendedInput) { if ($Entry.Type -eq 'Computer') { $Logger.AddInfoRecord("Computer $($Entry.Server) added to scan $($Entry.LogName) log for events: $($Entry.EventID -join ', ')") } else { $Logger.AddInfoRecord("File $($Entry.Server) added to scan $($Entry.LogName) log for events: $($Entry.EventID -join ', ')") } }
        $xmlTemplate = "$($($(Get-Module -ListAvailable PSWinReportingV2)[0]).ModuleBase)\Templates\Template-Collector.xml"
        if (Test-Path -LiteralPath $xmlTemplate) {
            $Logger.AddInfoRecord("Found Template $xmlTemplate")
            $SubscriptionCount = 0
            $InputServers = ($ExtendedInput | Group-Object -Property LogName)
            $ListTemplates = foreach ($InputData in $InputServers) {
                $Servers = $InputData.Group.Server
                $EventID = $InputData.Group.EventID | Select-Object -Unique
                $LogName = $InputData.Name
                $SplitArrayID = Split-Array -inArray $EventID -size 22
                $Array = foreach ($ID in $SplitArrayID) { Get-EventsFilter -ID $ID -LogName $LogName }
                foreach ($Events in $Array) {
                    $SubscriptionCount++
                    $SubscriptionTemplate = "$ENV:TEMP\PSWinReportingSubscription$SubscriptionCount.xml"
                    Copy-Item -Path $xmlTemplate -Destination $SubscriptionTemplate
                    $Logger.AddInfoRecord("Copied template $SubscriptionTemplate")
                    Add-ServersToXML -FilePath $SubscriptionTemplate -Servers $Servers
                    Set-XML -FilePath $SubscriptionTemplate -Path 'Subscription' -Node 'SubscriptionId' -Value "PSWinReporting Subscription Events - $SubscriptionCount"
                    Set-XML -FilePath $SubscriptionTemplate -Path 'Subscription' -Node 'ContentFormat' -Value 'Events'
                    Set-XML -FilePath $SubscriptionTemplate -Path 'Subscription' -Node 'ConfigurationMode' -Value 'Custom'
                    Set-XML -FilePath $SubscriptionTemplate -Path 'Subscription' -Node 'Query' -Value $Events
                    $SubscriptionTemplate
                }
            }
        } else { $Logger.AddInfoRecord("Template not found $xmlTemplate") }
        if ($AddTemplates) { Set-SubscriptionTemplates -ListTemplates $ListTemplates -DeleteOwn -LoggerParameters $LoggerParameters }
    }
    End { foreach ($Report in $Script:ReportDefinitions.Keys) { $Script:ReportDefinitions[$Report].Enabled = $false } }
}
[scriptblock] $SourcesAutoCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $Reports = (Get-EventsDefinitions).Keys
    $Reports | Sort-Object }
Register-ArgumentCompleter -CommandName New-WinSubscriptionTemplates -ParameterName Reports -ScriptBlock $SourcesAutoCompleter
function Remove-WinTaskScheduledForwarder {
    [CmdletBinding()]
    param([string] $TaskPath = '\Event Viewer Tasks\',
        [string] $TaskName = 'ForwardedEvents',
        [System.Collections.IDictionary] $LoggerParameters)
    if (-not $LoggerParameters) { $LoggerParameters = $Script:LoggerParameters }
    $Logger = Get-Logger @LoggerParameters
    try { Unregister-ScheduledTask -TaskPath $TaskPath -TaskName $TaskName -Confirm:$false -ErrorAction Stop } catch {
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        switch ($ErrorMessage) {
            { $_ -match 'No matching MSFT_ScheduledTask objects found by CIM query for instances of the' } { $Logger.AddInfoRecord("No tasks exists. Nothing to remove") }
            default { $Logger.AddErrorRecord("Tasks removal error: $ErrorMessage") }
        }
    }
}
function Start-WinNotifications {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Options,
        [System.Collections.IDictionary] $Definitions,
        [System.Collections.IDictionary] $Target,
        [int] $EventID,
        [int64] $EventRecordID,
        [string] $EventChannel)
    if ($Options.Logging) { $LoggerParameters = $Options.Logging } else { $LoggerParameters = $Script:LoggerParameters }
    $Logger = Get-Logger @LoggerParameters
    $Results = @{ }
    $Logger.AddInfoRecord("Executed Trigger for ID: $eventid and RecordID: $eventRecordID")
    $Logger.AddInfoRecord("Using Microsoft Teams: $($Options.Notifications.MicrosoftTeams.Enabled)")
    if ($Options.Notifications.MicrosoftTeams.Enabled) {
        foreach ($Priority in $Options.Notifications.MicrosoftTeams.Keys | Where-Object { $_ -notcontains 'Enabled' }) {
            [string] $URI = Format-FirstXChars -Text $Options.Notifications.MicrosoftTeams.$Priority.Uri -NumberChars 50
            $Logger.AddInfoRecord("Priority: $Priority, TeamsID: $URI...")
        }
    }
    $Logger.AddInfoRecord("Using Slack: $($Options.Notifications.Slack.Enabled)")
    if ($Options.Notifications.Slack.Enabled) {
        foreach ($Priority in $Options.Notifications.Slack.Keys | Where-Object { $_ -notcontains 'Enabled' }) {
            [string] $URI = Format-FirstXChars -Text $Options.Notifications.Slack.$Priority.URI -NumberChars 25
            $Logger.AddInfoRecord("Priority: $Priority, Slack URI: $URI...")
            $Logger.AddInfoRecord("Priority: $Priority, Slack Channel: $($($Options.Notifications.Slack.$Priority.Channel))...")
        }
    }
    $Logger.AddInfoRecord("Using Discord: $($Options.Notifications.Discord.Enabled)")
    if ($Options.Notifications.Discord.Enabled) {
        foreach ($Priority in $Options.Notifications.Discord.Keys | Where-Object { $_ -notcontains 'Enabled' }) {
            [string] $URI = Format-FirstXChars -Text $Options.Notifications.Discord.$Priority.URI -NumberChars 25
            $Logger.AddInfoRecord("Priority: $Priority, Discord URI: $URI...")
        }
    }
    $Logger.AddInfoRecord("Using MSSQL: $($Options.Notifications.MSSQL.Enabled)")
    if ($Options.Notifications.MSSQL.Enabled) {
        foreach ($Priority in $Options.Notifications.MSSQL.Keys | Where-Object { $_ -notcontains 'Enabled' }) {
            $Logger.AddInfoRecord("Priority: $Priority, Server\Instance: $($Options.Notifications.MSSQL.$Priority.SqlServer)")
            $Logger.AddInfoRecord("Priority: $Priority, Database: $($Options.Notifications.MSSQL.$Priority.SqlDatabase)")
        }
    }
    $Logger.AddInfoRecord("Using Email: $($Options.Notifications.Email.Enabled)")
    if ($Options.Notifications.Email.Enabled) { foreach ($Priority in $Options.Notifications.Email.Keys | Where-Object { 'Enabled', 'Formatting' -notcontains $_ }) { $Logger.AddInfoRecord("Priority: $Priority, Email TO: $($Options.Notifications.Email.$Priority.Parameters.To), Email CC: $($Options.Notifications.Email.$Priority.Parameters.CC)") } }
    if (-not $Options.Notifications.Slack.Enabled -and
        -not $Options.Notifications.MicrosoftTeams.Enabled -and
        -not $Options.Notifications.MSSQL.Enabled -and
        -not $Options.Notifications.Discord.Enabled -and
        -not $Options.Notifications.Email.Enabled) { return }
    [Array] $ExtendedInput = Get-ServersListLimited -Target $Target -RecordID $EventRecordID
    foreach ($Entry in $ExtendedInput) { if ($Entry.Type -eq 'Computer') { $Logger.AddInfoRecord("Computer $($Entry.Server) added to scan $($Entry.LogName) log for events: $($Entry.EventID -join ', ')") } else { $Logger.AddInfoRecord("File $($Entry.Server) added to scan $($Entry.LogName) log for events: $($Entry.EventID -join ', ')") } }
    $AllEvents = Get-Events -ExtendedInput $ExtendedInput -EventID $eventid -RecordID $eventRecordID -Verbose:$Options.Debug.Verbose
    foreach ($Report in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled' }) {
        if ($Definitions.$Report.Enabled) {
            $Logger.AddInfoRecord("Running $Report")
            $TimeExecution = Start-TimeLog
            foreach ($SubReport in $Definitions.$Report.Keys | Where-Object { $_ -notcontains 'Enabled', 'SqlExport' }) {
                if ($Definitions.$Report.$SubReport.Enabled) {
                    $Logger.AddInfoRecord("Running $Report with subsection $SubReport")
                    [string] $EventsType = $Definitions.$Report.$SubReport.LogName
                    [Array] $EventsNeeded = $Definitions.$Report.$SubReport.Events
                    [Array] $EventsFound = Get-EventsTranslation -Events $AllEvents -EventsDefinition $Definitions.$Report.$SubReport -EventIDs $EventsNeeded -EventsType $EventsType
                    $Logger.AddInfoRecord("Ending $Report with subsection $SubReport events found $($EventsFound.Count)")
                    $Results.$Report = $EventsFound
                }
            }
            $ElapsedTimeReport = Stop-TimeLog -Time $TimeExecution -Option OneLiner
            $Logger.AddInfoRecord("Ending $Report - Time to run $ElapsedTimeReport")
        }
    }
    [bool] $FoundPriorityEvent = $false
    foreach ($ReportName in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled', 'SqlExport', 'Priority' }) {
        if ($Results.$ReportName) {
            if ($null -ne $Definitions.$ReportName.Priority) {
                foreach ($Priority in $Definitions.$ReportName.Priority.Keys) {
                    [Array] $MyValue = Find-EventsTo -Prioritize -Events $Results.$ReportName -DataSet $Definitions.$ReportName.Priority.$Priority
                    if ($MyValue.Count) {
                        $Logger.AddInfoRecord("Sending event with $Priority priority.")
                        Send-Notificaton -Events $MyValue -Options $Options -Priority $Priority
                        $FoundPriorityEvent = $true
                    }
                }
            }
            if (-not $FoundPriorityEvent) {
                $Logger.AddInfoRecord("Sending event with default priority.")
                Send-Notificaton -Events $Results.$ReportName -Options $Options -Priority 'Default'
            }
        }
    }
    if ($Options.Backup.Enabled) { Protect-ArchivedLogs -TableEventLogClearedLogs $TableEventLogClearedLogs -DestinationPath $Options.Backup.DestinationPath -Verbose:$Options.Debug.Verbose }
}
function Start-WinReporting {
    [CmdletBinding()]
    param ([Parameter(Mandatory = $true)][System.Collections.IDictionary]$Times,
        [Parameter(Mandatory = $true)][alias('ReportOptions')][System.Collections.IDictionary] $Options,
        [Parameter(Mandatory = $true)][alias('ReportDefinitions')][System.Collections.IDictionary] $Definitions,
        [Parameter(Mandatory = $true)][alias('Servers', 'Computers')][System.Collections.IDictionary] $Target)
    if ($Options.Logging) { $LoggerParameters = $Options.Logging } else { $LoggerParameters = $Script:LoggerParameters }
    $Logger = Get-Logger @LoggerParameters
    $Dates = Get-ChoosenDates -ReportTimes $Times
    foreach ($Date in $Dates) {
        $Logger.AddInfoRecord("Starting to build a report for dates $($Date.DateFrom) to $($Date.DateTo)")
        Start-ReportSpecial -Dates $Date -Options $Options -Definitions $Definitions -Target $Target
    }
}
function Start-WinSubscriptionService {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $LoggerParameters)
    if (-not $LoggerParameters) { $LoggerParameters = $Script:LoggerParameters }
    $Logger = Get-Logger @LoggerParameters
    $Logger.AddInfoRecord('Starting Windows Event Collector service.')
    $Output = Start-MyProgram -Program $Script:ProgramWecutil -cmdArgList 'qc', '/q:true'
    $Logger.AddInfoRecord($Output)
}
Export-ModuleMember -Function @('Add-EventsDefinitions', 'Add-WinTaskScheduledForwarder', 'Find-Events', 'New-WinSubscriptionTemplates', 'Remove-WinTaskScheduledForwarder', 'Start-WinNotifications', 'Start-WinReporting', 'Start-WinSubscriptionService') -Alias @()