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, [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 = $Script:ReportTimes.Keys $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} else { $Reports = $PSBoundParameters.Report $DatesRange = $PSBoundParameters.DatesRange if (-not $Quiet) {$Logger.AddInfoRecord("Preparing reports: $($Reports -join ',')")} $Definitions = $Script:ReportDefinitions $Times = $Script:ReportTimes switch ($PSCmdlet.ParameterSetName) { DateRange {$Times.$DatesRange.Enabled = $true} DateManual { if ($DateFrom -and $DateTo) { $Times.CustomDate.Enabled = $true $Times.CustomDate.DateFrom = $DateFrom $Times.CustomDate.DateTo = $DateTo } else {return} } } $Dates = Get-ChoosenDates -ReportTimes $Times 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)")} if ($EventRecordID -ne 0 -and $EventId -ne 0) {[Array] $AllEvents = Get-Events -ExtendedInput $ExtendedInput -EventID $EventID -RecordID $EventRecordID -ErrorAction SilentlyContinue -ErrorVariable AllErrors -Verbose:$Verbose} else {[Array] $AllEvents = Get-Events -ExtendedInput $ExtendedInput -ErrorAction SilentlyContinue -ErrorVariable AllErrors -Verbose:$Verbose} 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 Find-ServersAD { [CmdletBinding()] param ([Object] $DC, [Object] $ReportDefinitions) $Servers = @() if ($ReportDefinitions.ReportsAD.Servers.Automatic -eq $true) { $DC = $DC | Where-Object {$_.'Host Name' -ne 'N/A'} if ($ReportDefinitions.ReportsAD.Servers.OnlyPDC -eq $true) {$Servers += ($DC | Where-Object {$_.'Is PDC' -eq 'Yes'}).'Host Name'} else {$Servers += $DC.'Host Name'} } else { if ($ReportDefinitions.ReportsAD.Servers.DC -eq '' -and $ReportDefinitions.ReportsAD.Servers.UseForwarders -eq $false) { $Logger.AddErrorRecord("Parameter ReportDefinitions.ReportsAD.Servers.DC is empty. Please choose Automatic or fill in this field") exit } else {$Servers += $ReportDefinitions.ReportsAD.Servers.DC} } return $Servers } function Get-AllRequiredEvents { [CmdletBinding()] param($Servers, [alias('File')][string] $FilePath, $Dates, $Events, [string] $LogName) $Verbose = ($PSCmdlet.MyInvocation.BoundParameters['Verbose'] -eq $true) $Count = Get-ObjectCount $Events if ($Count -ne 0) { if ($FilePath) {$MyEvents = Get-Events -Path $FilePath -DateFrom $Dates.DateFrom -DateTo $Dates.DateTo -EventID $Events -LogName $LogName -ErrorAction SilentlyContinue -ErrorVariable ErrorsReturned -Verbose:$Verbose} else {$MyEvents = Get-Events -Server $Servers -DateFrom $Dates.DateFrom -DateTo $Dates.DateTo -EventID $Events -LogName $LogName -ErrorAction SilentlyContinue -ErrorVariable ErrorsReturned -Verbose:$Verbose} foreach ($MyError in $ErrorsReturned) {Write-Error -Message $MyError} return $MyEvents } } function Get-ChoosenDates { 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-DC { [CmdletBinding()] param() try {$Forest = Get-ADForest} catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Logger.AddErrorRecord("Get-ADForest Error: $ErrorMessage") exit } $DCs = @() foreach ($DomainName in $Forest.Domains) { try {$Domain = Get-AdDomain -Server $DomainName} catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " switch ($ErrorMessage) { {$_ -match 'The server has rejected the client credentials'} {$Logger.AddErrorRecord('Domain Controller has rejected the client credentials. Please run this script with access to Domain Controllers')} {$_ -match 'Unable to find a default server with Active Directory Web Services running'} {$Logger.AddErrorRecord('Active Directory not found. Please run this script with access to Domain Controllers')} default {$Logger.AddErrorRecord("Get-DC Error for domain $DomainName`: $ErrorMessage")} } $DCs += [PsCustomObject][ordered] @{'Name' = 'Error' 'Domain' = $DomainName 'Host Name' = 'N/A' 'Operating System' = 'N/A' 'Site' = 'N/A' 'Ipv4' = 'N/A' 'Ipv6' = 'N/A' 'Is GC' = 'No' 'Is ReadOnly' = 'No' 'Is PDC' = 'No' 'Is Supported' = 'No' 'Is Included' = 'No' 'Enabled' = 'No' 'Comment' = "$ErrorMessage" } continue } try { $DomainControllers = Get-ADDomainController -Server $DomainName -Filter * foreach ($Policy in $DomainControllers) { $DCs += [PsCustomObject][ordered] @{'Name' = $Policy.Name 'Domain' = $DomainName 'Host Name' = $Policy.HostName 'Operating System' = $Policy.OperatingSystem 'Site' = $Policy.Site 'Ipv4' = if ($Policy.Ipv4Address -eq '') {'N/A'} else {$Policy.Ipv4Address} 'Ipv6' = if ($Policy.Ipv6Address -eq '') {'N/A'} else {$Policy.Ipv4Address} 'Is GC' = if ($Policy.IsGlobalCatalog) {'Yes'} else {'No'} 'Is ReadOnly' = if ($Policy.IsReadOnly) {'Yes'} else {'No'} 'Is PDC' = if ($Policy.HostName -eq $Domain.PDCEmulator) {'Yes'} else {'No'} 'Is Supported' = if ($Policy.OperatingSystem -notlike "*2003*" -and $Policy.OperatingSystem -notlike "*2000*") {'Yes'} else {'No'} 'Is Included' = '' 'Enabled' = if ($Policy.Enabled) {'Yes'} else {'No'} 'Comment' = 'OK' } } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " switch ($ErrorMessage) { {$_ -match 'The server has rejected the client credentials'} {$Logger.AddErrorRecord('Domain Controller has rejected the client credentials. Please run this script with access to Domain Controllers')} {$_ -match 'Unable to find a default server with Active Directory Web Services running'} {$Logger.AddErrorRecord('Active Directory not found. Please run this script with access to Domain Controllers')} default {$Logger.AddErrorRecord("Get-DC Error for domain $DomainName`: $ErrorMessage")} } } } return $DCs } 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-EventsData { [CmdletBinding()] param ([System.Collections.IDictionary] $ReportDefinitions, [string] $LogName) return Find-AllEvents -ReportDefinitions $ReportDefinitions -LogNameSearch $LogName | Sort-Object } 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) {$Events | Where-Object {$_.$Filter -eq $V}} } } $MyValue = foreach ($Event in $Events) { $IgnoredFound = $false $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 = Remove-WhiteSpace -Text $Entry 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]}} } } [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-MyEvents { [CmdLetBinding()] param([Array] $Events, [System.Collections.IDictionary] $ReportDefinition, [switch] $Quiet) 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('ReportName', [string], $ReportAttrib) $RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $RuntimeParamDic.Add('ReportName', $ReportRuntimeParam) return $RuntimeParamDic } Process { [string] $ReportName = $PSBoundParameters.ReportName [string] $ReportNameTitle = Format-AddSpaceToSentence -Text $ReportName if (-not $Quiet) {$Logger.AddInfoRecord("Running beautification process, applying filters for $ReportNameTitle report.")} $ExecutionTime = Start-TimeLog foreach ($Report in $ReportDefinition.Keys | Where-Object {$_ -ne 'Enabled'}) { $EventsType = $ReportDefinition[$Report].LogName $EventsNeeded = $ReportDefinition[$Report].Events $EventsFound = Find-EventsNeeded -Events $Events -EventsNeeded $EventsNeeded -EventsType $EventsType $EventsFound = Get-EventsTranslation -Events $EventsFound -EventsDefinition $ReportDefinition[$Report] $EventsFound } $EventsRemoved = $Events.Count - $EventsFound.Count $Elapsed = Stop-TimeLog -Time $ExecutionTime -Option OneLiner if (-not $Quiet) {$Logger.AddInfoRecord("Events returned: $($EventsFound.Count), events skipped: $EventsRemoved")} if (-not $Quiet) {$Logger.AddInfoRecord("Ending beautification process, applying filters for $ReportNameTitle report - Time elapsed: $Elapsed")} } } 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}}}} [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 = @{} } } 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 = @{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-DisplayParameters($ReportOptions, $DisplayProgress = $false) { $Test0 = Test-Key -ConfigurationTable $ReportOptions -ConfigurationKey 'DisplayConsole' -DisplayProgress $DisplayProgress if ($Test0 -eq $true) { $Test1 = Test-Key -ConfigurationTable $ReportOptions.DisplayConsole -ConfigurationSection '' -ConfigurationKey "ShowTime" -DisplayProgress $DisplayProgress $Test2 = Test-Key -ConfigurationTable $ReportOptions.DisplayConsole -ConfigurationSection '' -ConfigurationKey "LogFile" -DisplayProgress $DisplayProgress $Test3 = Test-Key -ConfigurationTable $ReportOptions.DisplayConsole -ConfigurationSection '' -ConfigurationKey "TimeFormat" -DisplayProgress $DisplayProgress if ($Test1 -and $Test2 -and $Test3) {$script:WriteParameters = $ReportOptions.DisplayConsole} } } 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-Report { [CmdletBinding()] param ([System.Collections.IDictionary] $Dates, [System.Collections.IDictionary] $EmailParameters, [System.Collections.IDictionary] $FormattingParameters, [System.Collections.IDictionary] $ReportOptions, [System.Collections.IDictionary] $ReportDefinitions) $time = [System.Diagnostics.Stopwatch]::StartNew() $EventLogTable = @() $TableEventLogFiles = @() $Logger.AddInfoRecord("Processing report for dates from: $($Dates.DateFrom) to $($Dates.DateTo)") $Logger.AddInfoRecord('Establishing servers list to process...') $ServersAD = Get-DC $Servers = Find-ServersAD -ReportDefinitions $ReportDefinitions -DC $ServersAD $Logger.AddInfoRecord('Preparing Security Events list to be processed') $EventsToProcessSecurity = Find-AllEvents -ReportDefinitions $ReportDefinitions -LogNameSearch 'Security' $Logger.AddInfoRecord('Preparing System Events list to be processed') $EventsToProcessSystem = Find-AllEvents -ReportDefinitions $ReportDefinitions -LogNameSearch 'System' $Logger.AddInfoRecord("Found security events to process: $($EventsToProcessSecurity -join ', ')") $Logger.AddInfoRecord("Found system events to process: $($EventsToProcessSystem -join ', ')") $AllErrors = @() $Events = New-ArrayList if ($ReportDefinitions.ReportsAD.Servers.UseForwarders) { $Logger.AddInfoRecord("Preparing Forwarded Events on forwarding servers: $($ReportDefinitions.ReportsAD.Servers.ForwardServer -join ', ')") foreach ($ForwardedServer in $ReportDefinitions.ReportsAD.Servers.ForwardServer) { $FoundEvents = Get-AllRequiredEvents -Servers $ForwardedServer -Dates $Dates -Events $EventsToProcessSecurity -LogName $ReportDefinitions.ReportsAD.Servers.ForwardEventLog -ErrorAction SilentlyContinue -ErrorVariable +AllErrors -Verbose:$ReportOptions.Debug.Verbose Add-ToArrayAdvanced -List $Events -Element $FoundEvents -SkipNull -Merge $FoundEvents = Get-AllRequiredEvents -Servers $ForwardedServer -Dates $Dates -Events $EventsToProcessSystem -LogName $ReportDefinitions.ReportsAD.Servers.ForwardEventLog -ErrorAction SilentlyContinue -ErrorVariable +AllErrors -Verbose:$ReportOptions.Debug.Verbose Add-ToArrayAdvanced -List $Events -Element $FoundEvents -SkipNull -Merge } } if ($ReportDefinitions.ReportsAD.Servers.UseDirectScan) { $Logger.AddInfoRecord("Processing Security Events from directly scanned servers: $($Servers -Join ', ')") $FoundEvents = Get-AllRequiredEvents -Servers $Servers -Dates $Dates -Events $EventsToProcessSecurity -LogName 'Security' -ErrorAction SilentlyContinue -ErrorVariable +AllErrors -Verbose:$ReportOptions.Debug.Verbose Add-ToArrayAdvanced -List $Events -Element $FoundEvents -SkipNull -Merge $Logger.AddInfoRecord("Processing System Events from directly scanned servers: $($Servers -Join ', ')") $FoundEvents = Get-AllRequiredEvents -Servers $Servers -Dates $Dates -Events $EventsToProcessSystem -LogName 'System' -ErrorAction SilentlyContinue -ErrorVariable +AllErrors -Verbose:$ReportOptions.Debug.Verbose Add-ToArrayAdvanced -List $Events -Element $FoundEvents -SkipNull -Merge } if ($ReportDefinitions.ReportsAD.ArchiveProcessing.Use) { $EventLogFiles = Get-EventLogFileList -Sections $ReportDefinitions.ReportsAD.ArchiveProcessing foreach ($File in $EventLogFiles) { $TableEventLogFiles += Get-FileInformation -File $File $Logger.AddInfoRecord("Processing Security Events on file: $File") $FoundEvents = Get-AllRequiredEvents -FilePath $File -Dates $Dates -Events $EventsToProcessSecurity -LogName 'Security' -ErrorAction SilentlyContinue -ErrorVariable +AllErrors -Verbose:$ReportOptions.Debug.Verbose Add-ToArrayAdvanced -List $Events -Element $FoundEvents -SkipNull -Merge $Logger.AddInfoRecord("Processing System Events on file: $File") $FoundEvents = Get-AllRequiredEvents -FilePath $File -Dates $Dates -Events $EventsToProcessSystem -LogName 'System' -ErrorAction SilentlyContinue -ErrorVariable +AllErrors -Verbose:$ReportOptions.Debug.Verbose Add-ToArrayAdvanced -List $Events -Element $FoundEvents -SkipNull -Merge } } foreach ($Errors in $AllErrors) {$Logger.AddErrorRecord($Errors)} $Logger.AddInfoRecord('Processing Event Log Sizes on defined servers for warnings') $EventLogDatesSummary = @() if ($ReportDefinitions.ReportsAD.Servers.UseForwarders) { $Logger.AddInfoRecord("Processing Event Log Sizes on $($ReportDefinitions.ReportsAD.Servers.ForwardServer) for warnings") $EventLogDatesSummary += Get-EventLogSize -Servers $ReportDefinitions.ReportsAD.Servers.ForwardServer -LogName $ReportDefinitions.ReportsAD.Servers.ForwardEventLog -Verbose:$ReportOptions.Debug.Verbose } if ($ReportDefinitions.ReportsAD.Servers.UseDirectScan) { $Logger.AddInfoRecord("Processing Event Log Sizes on $($Servers -Join ', ') for warnings") $EventLogDatesSummary += Get-EventLogSize -Servers $Servers -LogName 'Security' $EventLogDatesSummary += Get-EventLogSize -Servers $Servers -LogName 'System' } $Logger.AddInfoRecord('Verifying Warnings reported earlier') $Warnings = Invoke-EventLogVerification -Results $EventLogDatesSummary -Dates $Dates if ($ReportOptions.RemoveDuplicates.Enabled) { $Logger.AddInfoRecord("Removing Duplicates from all events. Current list contains $(Get-ObjectCount -Object $Events) events") $Events = Remove-DuplicateObjects -Object $Events -Property $ReportOptions.RemoveDuplicates.Properties $Logger.AddInfoRecord("Removed Duplicates Following $(Get-ObjectCount -Object $Events) events will be analyzed further") } $Results = @{} foreach ($ReportName in $ReportDefinitions.ReportsAD.EventBased.Keys) { if ($ReportDefinitions.ReportsAD.EventBased.$ReportName.Enabled -eq $true) { $Logger.AddInfoRecord("Running $ReportName Report") $TimeExecution = Start-TimeLog $Results.$ReportName = Get-EventsWorkaround -Events $Events -IgnoreWords $ReportDefinitions.ReportsAD.EventBased.$ReportName.IgnoreWords -Report $ReportName $ElapsedTime = Stop-TimeLog -Time $TimeExecution -Option OneLiner $Logger.AddInfoRecord("Ending $ReportName Report - Elapsed time: $ElapsedTime") } } if ($ReportDefinitions.ReportsAD.Custom.EventLogSize.Enabled -eq $true) { if ($ReportDefinitions.ReportsAD.Servers.UseForwarders) { foreach ($LogName in $ReportDefinitions.ReportsAD.Servers.ForwardEventLog) { $Logger.AddInfoRecord("Running Event Log Size Report for $LogName log") $EventLogTable += Get-EventLogSize -Servers $ReportDefinitions.ReportsAD.Servers.ForwardServer -LogName $LogName $Logger.AddInfoRecord("Ending Event Log Size Report for $LogName log") } } foreach ($LogName in $ReportDefinitions.ReportsAD.Custom.EventLogSize.Logs) { $Logger.AddInfoRecord("Running Event Log Size Report for $LogName log") $EventLogTable += Get-EventLogSize -Servers $Servers -LogName $LogName $Logger.AddInfoRecord("Ending Event Log Size Report for $LogName log") } if ($ReportDefinitions.ReportsAD.Custom.EventLogSize.SortBy -ne "") {$EventLogTable = $EventLogTable | Sort-Object $ReportDefinitions.ReportsAD.Custom.EventLogSize.SortBy} } $Logger.AddInfoRecord('Prepare email head and body') $HtmlHead = Set-EmailHead -FormattingOptions $FormattingParameters $HtmlBody = Set-EmailReportBranding -FormattingParameters $FormattingParameters $HtmlBody += Set-EmailReportDetails -FormattingParameters $FormattingParameters -Dates $Dates -Warnings $Warnings if ($ReportOptions.AsHTML.Use) { $HtmlBody += Export-ReportToHTML -Report $ReportDefinitions.ReportsAD.Custom.ServersData.Enabled -ReportTable $ServersAD -ReportTableText 'Following AD servers were detected in forest' $HtmlBody += Export-ReportToHTML -Report $ReportDefinitions.ReportsAD.Custom.FilesData.Enabled -ReportTable $TableEventLogFiles -ReportTableText 'Following files have been processed for events' $HtmlBody += Export-ReportToHTML -Report $ReportDefinitions.ReportsAD.Custom.EventLogSize.Enabled -ReportTable $EventLogTable -ReportTableText 'Following event log sizes were reported' foreach ($ReportName in $ReportDefinitions.ReportsAD.EventBased.Keys) { $ReportNameTitle = Format-AddSpaceToSentence -Text $ReportName -ToLowerCase $HtmlBody += Export-ReportToHTML -Report $ReportDefinitions.ReportsAD.EventBased.$ReportName.Enabled -ReportTable $Results.$ReportName -ReportTableText "Following $ReportNameTitle happened" } } $Reports = @() if ($ReportOptions.AsDynamicHTML.Use) { $ReportFileName = Set-ReportFile -FileNamePattern $ReportOptions.AsDynamicHTML.FilePattern -DateFormat $ReportOptions.AsDynamicHTML.DateFormat $DynamicHTML = New-HTML -TitleText $ReportOptions.AsDynamicHTML.Title -HideLogos:(-not $ReportOptions.AsDynamicHTML.Branding.Logo.Show) -RightLogoString $ReportOptions.AsDynamicHTML.Branding.Logo.RightLogo.ImageLink -UseCssLinks:$ReportOptions.AsDynamicHTML.EmbedCSS -UseStyleLinks:$ReportOptions.AsDynamicHTML.EmbedJS {foreach ($ReportName in $ReportDefinitions.ReportsAD.EventBased.Keys) { $ReportNameTitle = Format-AddSpaceToSentence -Text $ReportName if ($ReportDefinitions.ReportsAD.EventBased.$ReportName.Enabled) {New-HTMLContent -HeaderText $ReportNameTitle -CanCollapse {New-HTMLColumn -Columns 1 {if ($null -ne $Results.$ReportName) {Get-HTMLContentDataTable -ArrayOfObjects $Results.$ReportName -HideFooter}}}} }} if ($null -ne $DynamicHTML) { [string] $DynamicHTMLPath = Save-HTML -HTML $DynamicHTML -FilePath "$($ReportOptions.AsDynamicHTML.Path)\$ReportFileName" $Reports += $DynamicHTMLPath } } if ($ReportOptions.AsExcel) { $Logger.AddInfoRecord('Prepare XLSX files with Events') $ReportFilePathXLSX = Set-ReportFileName -ReportOptions $ReportOptions -ReportExtension "xlsx" Export-ReportToXLSX -Report $ReportDefinitions.ReportsAD.Custom.ServersData.Enabled -ReportOptions $ReportOptions -ReportFilePath $ReportFilePathXLSX -ReportName "Processed Servers" -ReportTable $ServersAD Export-ReportToXLSX -Report $ReportDefinitions.ReportsAD.Custom.EventLogSize.Enabled -ReportOptions $ReportOptions -ReportFilePath $ReportFilePathXLSX -ReportName "Event log sizes" -ReportTable $EventLogTable foreach ($ReportName in $ReportDefinitions.ReportsAD.EventBased.Keys) { $ReportNameTitle = Format-AddSpaceToSentence -Text $ReportName Export-ReportToXLSX -Report $ReportDefinitions.ReportsAD.EventBased.$ReportName.Enabled -ReportOptions $ReportOptions -ReportFilePath $ReportFilePathXLSX -ReportName $ReportNameTitle -ReportTable $Results.$ReportName } $Reports += $ReportFilePathXLSX } if ($ReportOptions.AsCSV) { $Logger.AddInfoRecord('Prepare CSV files with Events') $Reports += Export-ReportToCSV -Report $ReportDefinitions.ReportsAD.Custom.ServersData.Enabled -ReportOptions $ReportOptions -Extension "csv" -ReportName "ReportServers" -ReportTable $ServersAD $Reports += Export-ReportToCSV -Report $ReportDefinitions.ReportsAD.Custom.EventLogSize.Enabled -ReportOptions $ReportOptions -Extension "csv" -ReportName "ReportEventLogSize" -ReportTable $EventLogTable foreach ($ReportName in $ReportDefinitions.ReportsAD.EventBased.Keys) {$Reports += Export-ReportToCSV -Report $ReportDefinitions.ReportsAD.EventBased.$ReportName.Enabled -ReportOptions $ReportOptions -Extension "csv" -ReportName $ReportName -ReportTable $Results.$ReportName} } $Reports = $Reports | Where-Object {$_} | Sort-Object -Unique $Logger.AddInfoRecord('Prepare Email replacements and formatting') $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 $FormattingParameters -ConfigurationParameters $ReportOptions -Logger $Logger -SkipNewLines $EmailBody = $HtmlHead + $HtmlBody $Time.Stop() if ($ReportOptions.SendMail) { foreach ($Report in $Reports) {$Logger.AddInfoRecord("Following files will be attached to email $Report")} $TemporarySubject = $EmailParameters.EmailSubject -replace "<<DateFrom>>", "$($Dates.DateFrom)" -replace "<<DateTo>>", "$($Dates.DateTo)" $Logger.AddInfoRecord('Sending email with reports') if ($FormattingParameters.CompanyBranding.Inline) {$SendMail = Send-Email -EmailParameters $EmailParameters -Body $EmailBody -Attachment $Reports -Subject $TemporarySubject -InlineAttachments @{logo = $FormattingParameters.CompanyBranding.Logo} } else {$SendMail = Send-Email -EmailParameters $EmailParameters -Body $EmailBody -Attachment $Reports -Subject $TemporarySubject} if ($SendMail.Status) {$Logger.AddInfoRecord('Email successfully sent')} else {$Logger.AddInfoRecord("Error sending message: $($SendMail.Error)")} } if ($ReportOptions.AsHTML.Use) { $ReportHTMLPath = Set-ReportFileName -ReportOptions $ReportOptions -ReportExtension 'html' $EmailBody | Out-File -Encoding Unicode -FilePath $ReportHTMLPath $Logger.AddInfoRecord("Saving report to file: $ReportHTMLPath") if ($ReportHTMLPath -ne '' -and ($ReportOptions.AsHTML.OpenAsFile)) {if (Test-Path -LiteralPath $ReportHTMLPath) {Invoke-Item $ReportHTMLPath}} } if ($ReportOptions.AsDynamicHTML.Use -and $ReportOptions.AsDynamicHTML.OpenAsFile) {if ($DynamicHTMLPath -ne '' -and (Test-Path -LiteralPath $DynamicHTMLPath)) {Invoke-Item $DynamicHTMLPath}} foreach ($ReportName in $ReportDefinitions.ReportsAD.EventBased.Keys) { $ReportNameTitle = Format-AddSpaceToSentence -Text $ReportName Export-ReportToSql -Report $ReportDefinitions.ReportsAD.EventBased.$ReportName -ReportOptions $ReportOptions -ReportName $ReportNameTitle -ReportTable $Results.$ReportName } Remove-ReportsFiles -KeepReports $ReportOptions.KeepReports -ReportFiles $Reports } 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) { $ReportFileName = Set-ReportFile -Path $Options.AsDynamicHTML.Path -FileNamePattern $Options.AsDynamicHTML.FilePattern -DateFormat $Options.AsDynamicHTML.DateFormat $DynamicHTML = 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-HTMLContent -HeaderText $ReportNameTitle -CanCollapse {New-HTMLColumn -Columns 1 {if ($null -ne $Results.$ReportName) {New-HTMLTable -DataTable $Results.$ReportName -HideFooter}}}} }} if ($null -ne $DynamicHTML) { try { [string] $DynamicHTMLPath = Save-HTML -HTML $DynamicHTML -FilePath $ReportFileName 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) {if ($ReportHTMLPath -ne '' -and (Test-Path -LiteralPath $ReportHTMLPath)) {Invoke-Item -LiteralPath $ReportHTMLPath}} if ($Options.AsDynamicHTML.Enabled -and $Options.AsDynamicHTML.OpenAsFile) {if ($DynamicHTMLPath -ne '' -and (Test-Path -LiteralPath $DynamicHTMLPath)) {Invoke-Item -LiteralPath $DynamicHTMLPath}} if ($Options.AsExcel.Enabled -and $Options.AsExcel.OpenAsFile) {if ($ReportFilePathXLSX -ne '' -and (Test-Path -LiteralPath $ReportFilePathXLSX)) {Invoke-Item -LiteralPath $ReportFilePathXLSX}} if ($Options.AsCSV.Enabled -and $Options.AsCSV.OpenAsFile) {foreach ($CSV in $AttachCSV) {if ($CSV -ne '' -and (Test-Path -LiteralPath $CSV)) {Invoke-Item -LiteralPath $CSV}}} $AttachedReports = $AttachedReports | Where-Object {$_} | 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 @() |