PSWinReportingV2.psm1

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-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 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 = "$PSScriptRoot\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 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 Find-AllEvents {
    [CmdletBinding()]
    param ([System.Collections.IDictionary] $ReportDefinitions,
        [string] $LogNameSearch,
        [switch] $All)
    $EventsToProcess = foreach ($report in $ReportDefinitions.ReportsAD.EventBased.GetEnumerator()) {
        $ReportName = $report.Name
        $Enabled = $ReportDefinitions.ReportsAD.EventBased.$ReportName.Enabled
        $LogName = $ReportDefinitions.ReportsAD.EventBased.$ReportName.LogName
        $Events = $ReportDefinitions.ReportsAD.EventBased.$ReportName.Events
        if ($Enabled -eq $true -or $All -eq $true) {if ($LogNameSearch -eq $LogName) {$Events}}
    }
    return $EventsToProcess
}
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 ($Dates -is [Array]) {
                $Logger.AddErrorRecord("Currently only 1 date range is supported. Please fix Times and try again")
                return
            }
        } 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 ($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}
        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 ((Get-ObjectCount -Object $Reports) -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 Find-EventsNeeded {
    [CmdletBinding()]
    param ([Array] $Events,
        [alias('EventsNeeded')][Array] $EventIDs,
        [string] $EventsType = 'Security')
    $EventsFound = foreach ($Event in $Events) {if ($Event.LogName -eq $EventsType) {if ($EventIDs -contains $Event.ID) {$Event}}}
    return $EventsFound
}
function Find-EventsTo {
    [CmdletBinding()]
    param ([Array] $Events,
        [alias('IgnoreWords', 'PriorityWords')] $DataSet,
        [switch] $Ignore,
        [switch] $Prioritize)
    if ((Get-ObjectCount -Object $DataSet) -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-ChoosenDates {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $ReportTimes)
    $Dates = @(if ($ReportTimes.Contains('PastHour') -and $ReportTimes.PastHour.Enabled) {
            $DatesPastHour = Find-DatesPastHour
            if ($DatesPastHour -ne $null) {$DatesPastHour}
        }
        if ($ReportTimes.Contains('CurrentHour') -and $ReportTimes.CurrentHour.Enabled) {
            $DatesCurrentHour = Find-DatesCurrentHour
            if ($DatesCurrentHour -ne $null) {$DatesCurrentHour}
        }
        if ($ReportTimes.Contains('PastDay') -and $ReportTimes.PastDay.Enabled) {
            $DatesDayPrevious = Find-DatesDayPrevious
            if ($DatesDayPrevious -ne $null) {$DatesDayPrevious}
        }
        if ($ReportTimes.Contains('CurrentDay') -and $ReportTimes.CurrentDay.Enabled) {
            $DatesDayToday = Find-DatesDayToday
            if ($DatesDayToday -ne $null) {$DatesDayToday}
        }
        if ($ReportTimes.Contains('OnDay') -and $ReportTimes.OnDay.Enabled) {
            foreach ($Day in $ReportTimes.OnDay.Days) {
                $DatesReportOnDay = Find-DatesPastWeek $Day
                if ($DatesReportOnDay -ne $null) {$DatesReportOnDay}
            }
        }
        if ($ReportTimes.Contains('PastMonth') -and $ReportTimes.PastMonth.Enabled) {
            $DatesMonthPrevious = Find-DatesMonthPast -Force $ReportTimes.PastMonth.Force
            if ($DatesMonthPrevious -ne $null) {$DatesMonthPrevious}
        }
        if ($ReportTimes.Contains('CurrentMonth') -and $ReportTimes.CurrentMonth.Enabled) {
            $DatesMonthCurrent = Find-DatesMonthCurrent
            if ($DatesMonthCurrent -ne $null) {$DatesMonthCurrent}
        }
        if ($ReportTimes.Contains('PastQuarter') -and $ReportTimes.PastQuarter.Enabled) {
            $DatesQuarterLast = Find-DatesQuarterLast -Force $ReportTimes.PastQuarter.Force
            if ($DatesQuarterLast -ne $null) {$DatesQuarterLast}
        }
        if ($ReportTimes.Contains('CurrentQuarter') -and $ReportTimes.CurrentQuarter.Enabled) {
            $DatesQuarterCurrent = Find-DatesQuarterCurrent
            if ($DatesQuarterCurrent -ne $null) {$DatesQuarterCurrent}
        }
        if ($ReportTimes.Contains('CurrentDayMinusDayX') -and $ReportTimes.CurrentDayMinusDayX.Enabled) {
            $DatesCurrentDayMinusDayX = Find-DatesCurrentDayMinusDayX $ReportTimes.CurrentDayMinusDayX.Days
            if ($DatesCurrentDayMinusDayX -ne $null) {$DatesCurrentDayMinusDayX}
        }
        if ($ReportTimes.Contains('CurrentDayMinuxDaysX') -and $ReportTimes.CurrentDayMinuxDaysX.Enabled) {
            $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX $ReportTimes.CurrentDayMinuxDaysX.Days
            if ($DatesCurrentDayMinusDaysX -ne $null) {$DatesCurrentDayMinusDaysX}
        }
        if ($ReportTimes.Contains('CustomDate') -and $ReportTimes.CustomDate.Enabled) {
            $DatesCustom = @{DateFrom = $ReportTimes.CustomDate.DateFrom
                DateTo = $ReportTimes.CustomDate.DateTo
            }
            if ($DatesCustom -ne $null) {$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 -ne $null) {$DatesCurrentDayMinusDaysX}
        }
        if ($ReportTimes.Contains('Last7days') -and $ReportTimes.Last7days.Enabled) {
            $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX -days 7
            if ($DatesCurrentDayMinusDaysX -ne $null) {$DatesCurrentDayMinusDaysX}
        }
        if ($ReportTimes.Contains('Last14days') -and $ReportTimes.Last14days.Enabled) {
            $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX -days 14
            if ($DatesCurrentDayMinusDaysX -ne $null) {$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($Sections)
    $EventFiles = New-ArrayList
    if ($Sections.Contains("Directories")) {
        foreach ($Folder in $Sections.Directories.Keys) {
            $Files = Get-FilesInFolder -Folder $Sections.Directories.$Folder -Extension '*.evtx'
            foreach ($File in $Files) {Add-ToArrayAdvanced -List $EventFiles -Element $File -RequireUnique}
        }
    }
    if ($Sections.Contains("Files")) {
        foreach ($FileName in $Sections.Files.Keys) {
            $File = $($Sections.Files.$FileName)
            Add-ToArrayAdvanced -List $EventFiles -Element $File -RequireUnique
        }
    }
    return $EventFiles
}
function Get-EventLogSize {
    [CmdletBinding()]
    param([string[]] $Servers,
        [string] $LogName = "Security")
    $Results = foreach ($Server in $Servers) {
        $result = @{Server = $Server
            LogName = $LogName
        }
        try {
            $EventsInfo = Get-WinEvent -ListLog $LogName -ComputerName $Server
            $result.LogType = $EventsInfo.LogType
            $result.LogMode = $EventsInfo.LogMode
            $result.IsEnabled = $EventsInfo.IsEnabled
            $result.CurrentFileSize = Convert-Size -Value $EventsInfo.FileSize -From Bytes -To GB -Precision 2 -Display
            $result.MaximumFilesize = Convert-Size -Value $EventsInfo.MaximumSizeInBytes -From Bytes -To GB -Precision 2 -Display
            $result.EventOldest = (Get-WinEvent -MaxEvents 1 -LogName $LogName -Oldest -ComputerName $Server).TimeCreated
            $result.EventNewest = (Get-WinEvent -MaxEvents 1 -LogName $LogName -ComputerName $Server).TimeCreated
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            switch ($ErrorMessage) {
                {$_ -match 'No events were found'} {$Logger.AddErrorRecord("$Server Reading Event Log ($LogName) size failed. No events found.")}
                {$_ -match 'Attempted to perform an unauthorized operation'} {$Logger.AddErrorRecord("$Server Reading Event Log ($LogName) size failed. Unauthorized operation.")}
                default {$Logger.AddErrorRecord("$Server Reading Event Log ($LogName) size failed. Error occured: $ErrorMessage")}
            }
        }
        [PSCustomObject]$result | Select-Object Server, LogName, LogType, EventOldest, EventNewest, "CurrentFileSize", "MaximumFileSize", LogMode, IsEnabled
    }
    return $Results
}
function Get-EventsDefinitions {
    [CmdLetBinding()]
    param($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-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
            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 = Find-EventsNeeded -Events $AllEvents -EventIDs $EventsNeeded -EventsType $EventsType
                    [Array] $EventsFound = Get-EventsTranslation -Events $EventsFound -EventsDefinition $Definitions.$Report.$SubReport
                    if (-not $Quiet) {$Logger.AddInfoRecord("Ending $Report with subsection $SubReport events found $($EventsFound.Count)")}
                    $Results.$Report = $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)
    if ($EventsDefinition.Filter) {
        foreach ($Filter in $EventsDefinition.Filter.Keys) {
            $Value = $EventsDefinition.Filter[$Filter]
            $Events = foreach ($V in $Value) {foreach ($_ in $Events) {if ($_.$Filter -eq $V) {$_}}}
        }
    }
    $MyValue = foreach ($Event in $Events) {$HashTable = @{}
        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) {
                $OverwriteObject = $EventsDefinition.Overwrite.$Entry
                $StrippedEntry = $Entry -replace '#[0-9]{1,2}', ''
                if ($OverwriteObject.Count -eq 3) {if ($HashTable.($OverwriteObject[0]) -eq $OverwriteObject[1]) {$HashTable.$StrippedEntry = $OverwriteObject[2]}} elseif ($OverwriteObject.Count -eq 4) {if ($HashTable.($OverwriteObject[0]) -eq $OverwriteObject[1]) {$HashTable.$StrippedEntry = $OverwriteObject[2]} else {$HashTable.$StrippedEntry = $OverwriteObject[3]}}
            }
        }
        if ($null -ne $EventsDefinition.OverwriteByField) {
            foreach ($Entry in $EventsDefinition.OverwriteByField.Keys) {
                $OverwriteObject = $EventsDefinition.OverwriteByField.$Entry
                $StrippedEntry = $Entry -replace '#[0-9]{1,2}', ''
                if ($OverwriteObject.Count -eq 3) {if ($HashTable.($OverwriteObject[0]) -eq $OverwriteObject[1]) {$HashTable.$StrippedEntry = $HashTable.($OverwriteObject[2])}} elseif ($OverwriteObject.Count -eq 4) {if ($HashTable.($OverwriteObject[0]) -eq $OverwriteObject[1]) {$HashTable.$StrippedEntry = $HashTable.($OverwriteObject[2])} else {$HashTable.$StrippedEntry = $HashTable.($OverwriteObject[3])}}
            }
        }
        [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 Get-EventsWorkaround {
    [CmdLetBinding()]
    param([Array] $Events,
        [System.Collections.IDictionary] $IgnoreWords)
    DynamicParam {
        $Names = $Script:ReportDefinitions.Keys
        $ParamAttrib = New-Object System.Management.Automation.ParameterAttribute
        $ParamAttrib.Mandatory = $true
        $ParamAttrib.ParameterSetName = '__AllParameterSets'
        $ReportAttrib = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $ReportAttrib.Add($ParamAttrib)
        $ReportAttrib.Add((New-Object System.Management.Automation.ValidateSetAttribute($Names)))
        $ReportRuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Report', [string], $ReportAttrib)
        $RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $RuntimeParamDic.Add('Report', $ReportRuntimeParam)
        return $RuntimeParamDic
    }
    Process {
        [string] $ReportName = $PSBoundParameters.Report
        $MyReportDefinitions = $Script:ReportDefinitions.$ReportName
        foreach ($Report in $MyReportDefinitions.Keys | Where-Object {$_ -ne 'Enabled'}) {
            $MyReportDefinitions.$Report.IgnoreWords = $IgnoreWords
            $EventsType = $MyReportDefinitions[$Report].LogName
            $EventsNeeded = $MyReportDefinitions[$Report].Events
            $EventsFound = Find-EventsNeeded -Events $Events -EventsNeeded $EventsNeeded -EventsType $EventsType
            $EventsFound = Get-EventsTranslation -Events $EventsFound -EventsDefinition $MyReportDefinitions[$Report]
            $Logger.AddInfoRecord("Events Processed: $($EventsFound.Count), EventsType: $EventsType EventsID: $EventsNeeded")
            $EventsFound
        }
    }
}
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 Get-ServersPermissions {
    [CmdletBinding()]
    param ([string] $ProgramWevtutil,
        [string[]] $Servers,
        [string]$LogName = 'security')
    foreach ($DC in $Servers) {
        $cmdArgListGet = @("gl"
            $LogName
            "/r:$DC")
        Start-MyProgram -Program $Script:ProgramWevtutil -cmdArgList $cmdArgListGet
    }
}
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 Invoke-EventLogVerification {
    [CmdletBinding()]
    param($Results,
        $Dates)
    $Logs = @()
    foreach ($result in $Results) {
        if ($result.EventOldest -gt $Dates.DateFrom) {
            $Logger.AddWarningRecord("$($Result.Server)`: $($Result.LogName) log on doesn't cover whole date range requested. Oldest event $($Result.EventOldest) while requested $($Dates.DateFrom).")
            $Logs += "<strong>$($result.Server)</strong>`: <strong>$($result.LogName)</strong> log on doesn't cover whole date range requested. Oldest event <strong>$($result.EventOldest)</strong> while requested <strong>$($Dates.DateFrom)</strong>."
        }
    }
    return $Logs
}
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-EventQuery {
    [CmdletBinding()]
    param ([string[]]$Events,
        [string] $Type)
    Write-Verbose "New-EventQuery - Events Count: $($Events.Count)"
    $values = New-ArrayList
    Add-ToArray -List $Values -Element '<QueryList><Query Id="0" Path="Security">'
    Add-ToArray -List $Values -Element "<Select Path=`"$Type`">*[System[("
    foreach ($E in $Events) {
        Add-ToArray -List $Values -Element "EventID=$E"
        Add-ToArray -List $Values -Element "or"
    }
    Remove-FromArray -List $values -LastElement
    Add-ToArray -List $Values -Element ')]]</Select></Query></QueryList>'
    $FinalQuery = ([string] $Values)
    Write-Verbose $FinalQuery
    return ([string] $Values)
}
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 New-WinSubscriptionTemplates {
    [CmdletBinding()]
    param ([alias('ReportDefinitions')][System.Collections.IDictionary] $Definitions,
        [alias('Servers', 'Computers')][System.Collections.IDictionary] $Target,
        [System.Collections.IDictionary] $LoggerParameters,
        [switch] $AddTemplates)
    if (-not $LoggerParameters) {$LoggerParameters = $Script:LoggerParameters}
    $Logger = Get-Logger @LoggerParameters
    [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-Item $PSScriptRoot).Parent.FullName)\Templates\Template-Collector.xml"
    if (Test-Path -LiteralPath $xmlTemplate) {
        $Logger.AddInfoRecord("Found Template $xmlTemplate")
        $SubscriptionCount = 0
        $ListTemplates = foreach ($InputData in $ExtendedInput) {
            $SplitArrayID = Split-Array -inArray $InputData.EventID -size 22
            $Array = foreach ($ID in $SplitArrayID) {New-EventQuery -Events $ID -Type $InputData.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 $InputData.Server
                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}
}
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 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")}
        }
    }
}
$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 = @{'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 = $true
        OUEventsModify = @{Enabled = $true
            Events = 5136, 5137, 5139, 5141
            LogName = 'Security'
            Filter = @{'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 = @{'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 = @{'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 = @{'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 = @{}
            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 = @{'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 = @{'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 = @{'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 = @{'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'}
        }
    }
    ADEventsReboots = [ordered]@{Enabled = $false
        Events = @{Enabled = $true
            Events = 1001, 1018, 1, 12, 13, 42, 41, 109, 1, 6005, 6006, 6008, 6013
            LogName = 'System'
            IgnoreWords = @{}
        }
    }
}
$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 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 ($Property.Value -ne $null -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 $Events -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) {
                    $Logger.AddInfoRecord('Prepare email head and body')
                    $HtmlHead = Set-EmailHead -FormattingOptions $Options.AsHTML.Formatting
                    $HtmlBody = Set-EmailReportBranding -FormattingParameters $Options.AsHTML.Formatting
                    $HtmlBody += Export-ReportToHTML -Report $true-ReportTable $Events -ReportTableText "Quick notification event"
                    $HtmlBody = Set-EmailFormatting -Template $HtmlBody -FormattingParameters $Options.AsHTML.Formatting -ConfigurationParameters $Options -Logger $Logger -SkipNewLines
                    $HTML = $HtmlHead + $HtmlBody
                    $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.SendMail.Parameters.Subject -replace "<<DateFrom>>", "$($Dates.DateFrom)" -replace "<<DateTo>>", "$($Dates.DateTo)"
                    $Logger.AddInfoRecord('Sending email with reports')
                    if ($Options.Notifications.Email.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.AddInfoRecord("Error sending message: $($SendMail.Error)")}
                    Remove-ReportFiles -KeepReports $false -ReportFiles
                }
            }
        }
    }
    End {}
}
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-MissingDescription {
    [CmdletBinding()]
    param()
    $AllSubscriptions = Start-MyProgram -Program $ProgramWecutil -cmdArgList 'es'
    foreach ($Subscription in $AllSubscriptions) {
        $SubData = Start-MyProgram -Program $ProgramWecutil -cmdArgList 'gs', $Subscription
        Find-MyProgramData -Data $SubData -FindText 'ContentFormat*'
        $Change = Start-MyProgram -Program $ProgramWecutil -cmdArgList 'ss', $Subscription, '/cf:Events'
    }
}
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
}
function Set-ServersPermissions {
    [CmdletBinding()]
    param ($ProgramWevtutil,
        $Servers,
        [string]$LogName = 'security')
    foreach ($DC in $Servers) {
        $cmdArgListGet = @("gl"
            $LogName
            "/r:$DC")
        $cmdArgListSet = @("sl",
            $LogName
            "/r:$DC"
            "/ca:O:BAG:SYD:(A; ; 0xf0005; ; ; SY)(A; ; 0x5; ; ; BA)(A; ; 0x1; ; ; S-1-5-32-573)(A; ; 0x1; ; ; S-1-5-20)")
        Start-MyProgram -Program $Script:ProgramWevtutil -cmdArgList $cmdArgListSet
        Start-MyProgram -Program $Script:ProgramWevtutil -cmdArgList $cmdArgListGet
    }
}
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 Set-TimeReports {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $HashTable)
    $Reports = @()
    foreach ($reportName in $($HashTable.GetEnumerator().Name)) {$Reports += $reportName}
    $Count = 0
    foreach ($reportName in $reports) {if ($($HashTable[$reportName]).Count -ge $Count) {$Count = $($HashTable[$reportName]).Count}}
    $Count = $Count - 1
    $htmlStart = @"
    <table border="0" cellpadding="3" style="font-size:8pt;font-family:Segoe UI,Arial,sans-serif">
        <tr bgcolor="#009900">
            <th colspan="1">
                <font color="#ffffff">Report Names</font>
            </th>
            <th colspan="1">
                <font color="#ffffff">Total</font>
            </th>
        </tr>
"@

    foreach ($reportName in $reports) {
        $htmlStart += '<tr align="left" bgcolor="#dddddd">'
        $htmlStart += '<td>' + $reportName + '</td>'
        foreach ($ElapsedTime in $($HashTable[$reportName].GetEnumerator())) {$htmlStart += '<td>' + $ElapsedTime.Value + '</td>'}
        $htmlStart += '</tr>'
    }
    $htmlStart += '</table>'
    return $htmlStart
}
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 ', ')")}}
    $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 $(Get-ObjectCount -Object $AllEvents) events")
        $AllEvents = Remove-DuplicateObjects -Object $AllEvents -Property $Options.RemoveDuplicates.Properties
        $Logger.AddInfoRecord("Removed Duplicates Following $(Get-ObjectCount -Object $AllEvents) 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 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 = Find-EventsNeeded -Events $AllEvents -EventIDs $EventsNeeded -EventsType $EventsType
                    [Array] $EventsFound = Get-EventsTranslation -Events $EventsFound -EventsDefinition $Definitions.$Report.$SubReport
                    $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) {
                    $MyValue = Find-EventsTo -Prioritize -Events $Results.$ReportName -DataSet $Definitions.$ReportName.Priority.$Priority
                    if ((Get-ObjectCount -Object $MyValue) -gt 0) {
                        $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)
}
function Test-Configuration () {
    [CmdletBinding()]
    param ([Parameter(Mandatory = $true)]
        [System.Collections.IDictionary]$LoggerParameters,
        [Parameter(Mandatory = $true)]
        [System.Collections.IDictionary]$EmailParameters,
        [Parameter(Mandatory = $true)]
        [System.Collections.IDictionary]$FormattingParameters,
        [Parameter(Mandatory = $true)]
        [System.Collections.IDictionary]$ReportOptions,
        [Parameter(Mandatory = $true)]
        [System.Collections.IDictionary]$ReportTimes,
        [Parameter(Mandatory = $true)]
        [System.Collections.IDictionary]$ReportDefinitions)
    $Logger.AddInfoRecord('Testing for configuration consistency. This is to make sure the script can be safely executed.')
    $Success = $true
    $Success = (Test-Key $LoggerParameters "LoggerParameters" "ShowTime" -DisplayProgress) -and $Success
    $Success = (Test-Key $LoggerParameters "LoggerParameters" "LogsDir" -DisplayProgress) -and $Success
    $Success = (Test-Key $LoggerParameters "LoggerParameters" "TimeFormat" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailFrom" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailTo" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailCC" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailBCC" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailServer" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailServerPassword" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailServerPort" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailServerLogin" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailServerEnableSSL" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailEncoding" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailSubject" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailPriority" -DisplayProgress) -and $Success
    $Success = (Test-Key $EmailParameters "EmailParameters" "EmailReplyTo" -DisplayProgress) -and $Success
    $Success = (Test-Key $FormattingParameters "FormattingParameters" "CompanyBranding" -DisplayProgress) -and $Success
    if (Test-Key $FormattingParameters "FormattingParameters" "CompanyBranding") {
        $Success = (Test-Key $FormattingParameters.CompanyBranding "FormattingParameters.CompanyBranding" "Logo" -DisplayProgress) -and $Success
        $Success = (Test-Key $FormattingParameters.CompanyBranding "FormattingParameters.CompanyBranding" "Inline" -DisplayProgress) -and $Success
        $Success = (Test-Key $FormattingParameters.CompanyBranding "FormattingParameters.CompanyBranding" "Width" -DisplayProgress) -and $Success
        $Success = (Test-Key $FormattingParameters.CompanyBranding "FormattingParameters.CompanyBranding" "Height" -DisplayProgress) -and $Success
        $Success = (Test-Key $FormattingParameters.CompanyBranding "FormattingParameters.CompanyBranding" "Link" -DisplayProgress) -and $Success
    }
    $Success = (Test-Key $FormattingParameters "FormattingParameters" "FontFamily" -DisplayProgress) -and $Success
    $Success = (Test-Key $FormattingParameters "FormattingParameters" "FontSize" -DisplayProgress) -and $Success
    $Success = (Test-Key $FormattingParameters "FormattingParameters" "FontHeadingFamily" -DisplayProgress) -and $Success
    $Success = (Test-Key $FormattingParameters "FormattingParameters" "FontHeadingSize" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions "ReportOptions" "JustTestPrerequisite" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions "ReportOptions" "AsExcel" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions "ReportOptions" "AsCSV" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions "ReportOptions" "AsHTML" -DisplayProgress) -and $Success
    if (Test-Key $ReportOptions "ReportOptions" "AsHTML") {
        $Success = (Test-Key $ReportOptions.AsHTML "ReportOptions.AsHTML" "Use" -DisplayProgress -ValueType 'Boolean') -and $Success
        $Success = (Test-Key $ReportOptions.AsHTML "ReportOptions.ASHTML" "OpenAsFile" -DisplayProgress -ValueType 'Boolean') -and $Success
    }
    $Success = (Test-Key $ReportOptions "ReportOptions" "AsDynamicHTML" -DisplayProgress) -and $Success
    if (Test-Key $ReportOptions "ReportOptions" "AsDynamicHTML") {
        $Success = (Test-Key $ReportOptions.AsDynamicHTML "ReportOptions.AsDynamicHTML" "Use" -DisplayProgress -ValueType 'Boolean') -and $Success
        $Success = (Test-Key $ReportOptions.AsDynamicHTML "ReportOptions.AsDynamicHTML" "OpenAsFile" -DisplayProgress -ValueType 'Boolean') -and $Success
        $Success = (Test-Key $ReportOptions.AsDynamicHTML "ReportOptions.AsDynamicHTML" "Title" -DisplayProgress -ValueType 'string') -and $Success
        $Success = (Test-Key $ReportOptions.AsDynamicHTML "ReportOptions.AsDynamicHTML" "Path" -DisplayProgress -ValueType 'string') -and $Success
        $Success = (Test-Key $ReportOptions.AsDynamicHTML "ReportOptions.AsDynamicHTML" "FilePattern" -DisplayProgress -ValueType 'string') -and $Success
        $Success = (Test-Key $ReportOptions.AsDynamicHTML "ReportOptions.AsDynamicHTML" "DateFormat" -DisplayProgress -ValueType 'string') -and $Success
        $Success = (Test-Key $ReportOptions.AsDynamicHTML "ReportOptions.AsDynamicHTML" "EmbedCSS" -DisplayProgress -ValueType 'Boolean') -and $Success
        $Success = (Test-Key $ReportOptions.AsDynamicHTML "ReportOptions.AsDynamicHTML" "EmbedJS" -DisplayProgress -ValueType 'Boolean') -and $Success
    }
    $Success = (Test-Key $ReportOptions "ReportOptions" "SendMail" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions "ReportOptions" "KeepReports" -DisplayProgress) -and $Success
    if (Test-Key $ReportOptions "ReportOptions" "KeepReports") {
        if (-not (Test-Path $ReportOptions.KeepReportsPath -PathType Container)) {
            $Success = $false
            $Logger.AddErrorRecord('Path in configuration of ReportOptions.KeepReportsPath doesn''t exist.')
        }
    }
    $Success = (Test-Key $ReportOptions "ReportOptions" "FilePattern" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions "ReportOptions" "FilePatternDateFormat" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions "ReportOptions" "RemoveDuplicates" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions "ReportOptions" "Debug" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions.Debug "ReportOptions.Debug" "DisplayTemplateHTML" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportOptions.Debug "ReportOptions.Debug" "Verbose" -DisplayProgress) -and $Success
    if ($ReportOptions.Contains("AsSql")) {
        $Success = (Test-Key $ReportOptions.AsSql "ReportOptions.AsSql" "Use" -DisplayProgress) -and $Success
        if ($ReportOptions.AsSql.Contains("Use") -and $ReportOptions.AsSql.Use) {
            $Success = (Test-Key $ReportOptions.AsSql "ReportOptions.AsSql" "SqlServer" -DisplayProgress) -and $Success
            $Success = (Test-Key $ReportOptions.AsSql "ReportOptions.AsSql" "SqlDatabase" -DisplayProgress) -and $Success
            $Success = (Test-Key $ReportOptions.AsSql "ReportOptions.AsSql" "SqlTable" -DisplayProgress) -and $Success
            $Success = (Test-Key $ReportOptions.AsSql "ReportOptions.AsSql" "SqlTableCreate" -DisplayProgress) -and $Success
            $Success = (Test-Key $ReportOptions.AsSql "ReportOptions.AsSql" "SqlTableAlterIfNeeded" -DisplayProgress) -and $Success
            $Success = (Test-Key $ReportOptions.AsSql "ReportOptions.AsSql" "SqlCheckBeforeInsert" -DisplayProgress) -and $Success
        }
    }
    $Success = (Test-Key $ReportDefinitions "ReportDefinitions" "ReportsAD" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD "ReportDefinitions.ReportsAD" "Servers" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Servers "ReportDefinitions.ReportsAD.Servers" "UseForwarders" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Servers "ReportDefinitions.ReportsAD.Servers" "ForwardServer" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Servers "ReportDefinitions.ReportsAD.Servers" "ForwardEventLog" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Servers "ReportDefinitions.ReportsAD.Servers" "UseDirectScan" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Servers "ReportDefinitions.ReportsAD.Servers" "Automatic" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Servers "ReportDefinitions.ReportsAD.Servers" "OnlyPDC" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Servers "ReportDefinitions.ReportsAD.Servers" "DC" -DisplayProgress) -and $Success
    if (Test-Key $ReportDefinitions.ReportsAD "ReportDefinitions.ReportsAD" "ArchiveProcessing") {
        if (Test-Key $ReportDefinitions.ReportsAD.ArchiveProcessing "ReportDefinitions.ReportsAD.ArchiveProcessing" "Directories" -DisplayProgress) {
            foreach ($Folder in $ReportDefinitions.ReportsAD.ArchiveProcessing.Directories.Values) {
                if (-not (Test-Path $Folder -PathType Container)) {
                    $Success = $false
                    $Logger.AddErrorRecord('Path in configuration of ReportDefinitions.ReportsAD.ArchiveProcessing.Directories doesn''t exist.')
                }
            }
        }
        if (Test-Key $ReportDefinitions.ReportsAD.ArchiveProcessing "ReportDefinitions.ReportsAD.ArchiveProcessing" "Files" -DisplayProgress) {
            foreach ($File in $ReportDefinitions.ReportsAD.ArchiveProcessing.Files.Values) {
                if (-not (Test-Path $File -PathType Leaf)) {
                    $Success = $false
                    $Logger.AddErrorRecord('Path in configuration of ReportDefinitions.ReportsAD.ArchiveProcessing.Files doesn''t exist.')
                }
            }
        }
    }
    $Success = (Test-Key $ReportDefinitions.ReportsAD "ReportDefinitions.ReportsAD" "EventBased" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.EventBased "ReportDefinitions.ReportsAD.EventBased" "UserChanges" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.EventBased.UserChanges "ReportDefinitions.ReportsAD.EventBased.UserChanges" "Enabled" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.EventBased.UserChanges "ReportDefinitions.ReportsAD.EventBased.UserChanges" "Events" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.EventBased.UserChanges "ReportDefinitions.ReportsAD.EventBased.UserChanges" "LogName" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.EventBased.UserChanges "ReportDefinitions.ReportsAD.EventBased.UserChanges" "IgnoreWords" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD "ReportDefinitions.ReportsAD" "Custom" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Custom "ReportDefinitions.ReportsAD.Custom" "EventLogSize" -DisplayProgress) -and $Success
    if (Test-Key $ReportDefinitions.ReportsAD.Custom "ReportDefinitions.ReportsAD.Custom" "EventLogSize") {
        $Success = (Test-Key $ReportDefinitions.ReportsAD.Custom.EventLogSize "ReportDefinitions.ReportsAD.Custom.EventLogSize" "Enabled" -DisplayProgress) -and $Success
        $Success = (Test-Key $ReportDefinitions.ReportsAD.Custom.EventLogSize "ReportDefinitions.ReportsAD.Custom.EventLogSize" "Logs" -DisplayProgress) -and $Success
        $Success = (Test-Key $ReportDefinitions.ReportsAD.Custom.EventLogSize "ReportDefinitions.ReportsAD.Custom.EventLogSize" "SortBy" -DisplayProgress) -and $Success
    }
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Custom "ReportDefinitions.ReportsAD.Custom" "ServersData" -DisplayProgress) -and $Success
    if (Test-Key $ReportDefinitions.ReportsAD.Custom "ReportDefinitions.ReportsAD.Custom" "ServersData") {$Success = (Test-Key $ReportDefinitions.ReportsAD.Custom.ServersData "ReportDefinitions.ReportsAD.Custom.ServersData" "Enabled" -DisplayProgress) -and $Success}
    $Success = (Test-Key $ReportDefinitions.ReportsAD.Custom "ReportDefinitions.ReportsAD.Custom" "FilesData" -DisplayProgress) -and $Success
    if (Test-Key $ReportDefinitions.ReportsAD.Custom "ReportDefinitions.ReportsAD.Custom" "FilesData") {$Success = (Test-Key $ReportDefinitions.ReportsAD.Custom.ServersData "ReportDefinitions.ReportsAD.Custom.FilesData" "Enabled" -DisplayProgress) -and $Success}
    $Success = (Test-Key $ReportTimes "ReportTimes" "PastHour" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "PastHour") {$Success = (Test-Key $ReportTimes.PastHour "ReportTimes.PastHour" "Enabled" -DisplayProgress -ValueType 'Boolean') -and $Success}
    $Success = (Test-Key $ReportTimes "ReportTimes" "CurrentHour" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "CurrentHour") {$Success = (Test-Key $ReportTimes.CurrentHour "ReportTimes.CurrentHour" "Enabled" -DisplayProgress -ValueType 'Boolean') -and $Success}
    $Success = (Test-Key $ReportTimes "ReportTimes" "PastDay" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "PastDay") {$Success = (Test-Key $ReportTimes.PastDay "ReportTimes.PastDay" "Enabled" -DisplayProgress -ValueType 'Boolean') -and $Success}
    $Success = (Test-Key $ReportTimes "ReportTimes" "CurrentDay" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "CurrentDay") {$Success = (Test-Key $ReportTimes.CurrentDay "ReportTimes.CurrentDay" "Enabled" -DisplayProgress -ValueType 'Boolean') -and $Success}
    $Success = (Test-Key $ReportTimes "ReportTimes" "OnDay" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "OnDay") {
        $Success = (Test-Key $ReportTimes.OnDay "ReportTimes.OnDay" "Enabled" -DisplayProgress) -and $Success
        $Success = (Test-Key $ReportTimes.OnDay "ReportTimes.OnDay" "Days" -DisplayProgress) -and $Success
    }
    $Success = (Test-Key $ReportTimes "ReportTimes" "PastMonth" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "PastMonth") {
        $Success = (Test-Key $ReportTimes.PastMonth "ReportTimes.PastMonth" "Enabled" -DisplayProgress) -and $Success
        $Success = (Test-Key $ReportTimes.PastMonth "ReportTimes.PastMonth" "Force" -DisplayProgress) -and $Success
    }
    $Success = (Test-Key $ReportTimes "ReportTimes" "CurrentMonth" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportTimes "ReportTimes" "PastQuarter" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "PastQuarter") {
        $Success = (Test-Key $ReportTimes.PastQuarter "ReportTimes.PastQuarter" "Enabled" -DisplayProgress) -and $Success
        $Success = (Test-Key $ReportTimes.PastQuarter "ReportTimes.PastQuarter" "Force" -DisplayProgress) -and $Success
    }
    $Success = (Test-Key $ReportTimes "ReportTimes" "CurrentQuarter" -DisplayProgress) -and $Success
    $Success = (Test-Key $ReportTimes "ReportTimes" "CurrentDayMinusDayX" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "CurrentDayMinusDayX") {
        $Success = (Test-Key $ReportTimes.CurrentDayMinusDayX "ReportTimes.CurrentDayMinusDayX" "Enabled" -DisplayProgress) -and $Success
        $Success = (Test-Key $ReportTimes.CurrentDayMinusDayX "ReportTimes.CurrentDayMinusDayX" "Days" -DisplayProgress) -and $Success
    }
    $Success = (Test-Key $ReportTimes "ReportTimes" "CurrentDayMinuxDaysX" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "CurrentDayMinuxDaysX") {
        $Success = (Test-Key $ReportTimes.CurrentDayMinuxDaysX "ReportTimes.CurrentDayMinuxDaysX" "Enabled" -DisplayProgress) -and $Success
        $Success = (Test-Key $ReportTimes.CurrentDayMinuxDaysX "ReportTimes.CurrentDayMinuxDaysX" "Days" -DisplayProgress) -and $Success
    }
    $Success = (Test-Key $ReportTimes "ReportTimes" "CustomDate" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "CustomDate") {
        $Success = (Test-Key $ReportTimes.CustomDate "ReportTimes.CustomDate" "Enabled" -DisplayProgress) -and $Success
        $Success = (Test-Key $ReportTimes.CustomDate "ReportTimes.CustomDate" "DateFrom" -DisplayProgress) -and $Success
        $Success = (Test-Key $ReportTimes.CustomDate "ReportTimes.CustomDate" "DateTo" -DisplayProgress) -and $Success
    }
    $Success = (Test-Key $ReportTimes "ReportTimes" "Everything" -DisplayProgress) -and $Success
    if (Test-Key $ReportTimes "ReportTimes" "Everything") {$Success = (Test-Key $ReportTimes.PastDay "ReportTimes.Everything" "Enabled" -DisplayProgress -ValueType 'Boolean') -and $Success}
    return $Success
}
function Test-Key () {
    [CmdletBinding()]
    param ([Parameter(Mandatory = $true)]
        [Object] $ConfigurationTable,
        [Parameter(Mandatory = $true)]
        [string] $ConfigurationSection,
        [Parameter(Mandatory = $true)]
        [string] $ConfigurationKey,
        [string] $ValueType,
        [switch] $DisplayProgress)
    Write-Verbose "Test-Key ConfigurationKey: $ConfigurationKey, ConfigurationSection: $ConfigurationSection, ValueType: $ValueType, DisplayProgress: $DisplayProgress"
    if ($ConfigurationTable -is [System.Collections.IDictionary]) {
        if (-not $ConfigurationTable.Contains($ConfigurationKey)) {
            if ($DisplayProgress) {$Logger.AddErrorRecord("Parameter $ConfigurationKey in configuration of $ConfigurationSection doesn't exists.")}
            return $false
        }
    }
    if (-not $PSBoundParameters.ContainsKey('ValueType')) {
        if ($DisplayProgress) {$Logger.AddSuccessRecord("Parameter $ConfigurationKey in configuration of $ConfigurationSection exists")}
        return $true
    }
    if ($null -ne $ConfigurationTable.$ConfigurationKey) {
        if (-not ($ConfigurationTable.$ConfigurationKey.GetType().Name -eq $ValueType)) {
            if ($DisplayProgress) {$Logger.AddErrorRecord("Parameter $ConfigurationKey in configuration of $ConfigurationSection exists but the type of key is incorrect")}
            return $false
        }
    } else {
        if ($DisplayProgress) {$Logger.AddErrorRecord("Parameter $ConfigurationKey in configuration of $ConfigurationSection doesn't exists.")}
        return $false
    }
    if ($DisplayProgress) {$Logger.AddSuccessRecord("Parameter $ConfigurationKey in configuration of $ConfigurationSection exists and correct")}
    return $true
}
function Test-Modules () {
    [CmdletBinding()]
    param ([System.Collections.IDictionary] $ReportOptions)
    $Logger.AddInfoRecord('Testing for prerequisite availability')
    $ImportPSEventViewer = Get-ModulesAvailability -Name "PSEventViewer"
    if ($ImportPSEventViewer -eq $true) {$Logger.AddSuccessRecord('PSEventViewer module imported')} else {$Logger.AddErrorRecord('PSEventViewer module not found')}
    $ImportPSADReporting = Get-ModulesAvailability -Name "PSWinReporting"
    if ($ImportPSADReporting) {$Logger.AddSuccessRecord('PSWinReporting module imported')} else {$Logger.AddErrorRecord('PSWinReporting module not found')}
    $ImportExcel = Get-ModulesAvailability -Name "PSWriteExcel"
    if ($ImportExcel) {$Logger.AddSuccessRecord('PSWriteExcel module imported')} else {
        $Logger.AddInfoRecord('PSWriteExcel module not found')
        if ($ReportOptions.AsExcel) {
            $Logger.AddErrorRecord('PSWriteExcel module is not installed. Disable AsExcel under ReportOptions option before rerunning this script')
            $Logger.AddInfoRecord('Alternatively run Install-Module -Name PSWriteExcel before re-running this script. It''s quite useful module!')
            $Logger.AddInfoRecord('If Install-Module is not there as well you need to download PackageManagement PowerShell Modules')
            $Logger.AddInfoRecord('It can be found at https://www.microsoft.com/en-us/download/details.aspx?id=51451. After download, install and re-run Install-Module again.')
        }
    }
    $ImportActiveDirectory = Get-ModulesAvailability -Name "ActiveDirectory"
    if ($ImportActiveDirectory) {$Logger.AddSuccessRecord('ActiveDirectory module imported')} else {
        $Logger.AddErrorRecord('ActiveDirectory module not found')
        $Logger.AddInfoRecord('Please make sure it''s available on the machine before running this script')
    }
    return ($ImportPSEventViewer -and $ImportPSADReporting -and $ImportActiveDirectory -and (($ReportOptions.AsExcel -and $ImportExcel) -or (-not $ReportOptions.AsExcel)))
}
Export-ModuleMember -Function @('Add-EventsDefinitions', 'Add-WinTaskScheduledForwarder', 'Find-Events', 'New-WinSubscriptionTemplates', 'Remove-WinTaskScheduledForwarder', 'Start-WinNotifications', 'Start-WinReporting', 'Start-WinSubscriptionService') -Alias @()