PSWinReporting.psm1
function Add-ServersToXML { param ( [string] $FilePath, [string[]] $Servers ) #$doc = New-Object System.Xml.XmlDocument #$doc.Load($filePath) [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) $xmlDocument.Subscription.Eventsources.AppendChild($node) > $null $xmlDocument.Subscription.Eventsources.EventSource.AppendChild($nodeServer) > $null } Save-XML -FilePath $FilePath -xml $xmlDocument } function Add-TaskScheduledForwarder { [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 $xmlTemplate) { Write-Color 'Found Template ', $xmlTemplate -Color White, Yellow $ListTemplates = New-ArrayList if (Test-Path $xmlTemplate) { $ScheduledTaskXML = "$ENV:TEMP\PSWinReportingSchedluledTask.xml" Copy-Item -Path $xmlTemplate $ScheduledTaskXML Write-Color 'Copied template ', $ScheduledTaskXML -Color White, Yellow 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 $ScheduledTaskXML | out-string) try { $Output = Register-ScheduledTask -TaskPath $TaskPath -TaskName $TaskName -xml $xml } 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) { # checks if Report is enabled if ($ReportOptions.Contains('AsSql') -and $ReportOptions.AsSql.Use) { # checks if global sql is enabled if ($Report.Contains('EnabledSqlGlobal') -and $Report.EnabledSqlGlobal) { # checks if global sql is enabled for particular dataset $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) { # checks if local sql is enabled for dataset $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 { 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) { # checks if Report is enabled on global level and for that particular report if ($ReportOptions.Contains('AsSql') -and $ReportOptions.AsSql.Enabled -and $Report.Contains('SqlExport') -and $Report.SqlExport.EnabledGlobal) { # checks if global sql is enabled for particular dataset $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) { # checks if local sql is enabled for dataset $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 { 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 #$IgnoreWords = $ReportDefinitions.ReportsAD.EventBased.$ReportName.IgnoreWords if ($Enabled -eq $true -or $All -eq $true) { if ($LogNameSearch -eq $LogName) { $EventsToProcess += $Events } } } return $EventsToProcess } function Find-Events { [CmdLetBinding()] param( [parameter(ParameterSetName = "DateManual")][DateTime] $DateFrom, [parameter(ParameterSetName = "DateManual")][DateTime] $DateTo, [alias('Server', 'ComputerName')][string[]] $Servers = $Env:COMPUTERNAME, [alias('RunAgainstDC')][switch] $DetectDC, [switch] $Quiet, [System.Collections.IDictionary] $LoggerParameters ) DynamicParam { # Defines Report / Dates Range dynamically from HashTables $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) $DatesRange = $Script:ReportTimes.Keys $ParamAttribDatesRange = New-Object System.Management.Automation.ParameterAttribute $ParamAttribDatesRange.Mandatory = $true $ParamAttribDatesRange.ParameterSetName = 'DateRange' $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 { if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) { $Verbose = $true } else { $Verbose = $false } $Report = $PSBoundParameters.Report $DatesRange = $PSBoundParameters.DatesRange # Bring defaults $ReportTimes = $Script:ReportTimes $ReportDefinitions = $Script:ReportDefinitions ## Logging / Display to screen if (-not $LoggerParameters) { $LoggerParameters = $Script:LoggerParameters } $Logger = Get-Logger @LoggerParameters ## if ($DetectDC) { $ServersAD = Get-DC $Servers = ($ServersAD | Where-Object { $_.'Host Name' -ne 'N/A' }).'Host Name' } switch ($PSCmdlet.ParameterSetName) { DateRange { $ReportTimes.$DatesRange.Enabled = $true } DateManual { if ($DateFrom -and $DateTo) { $ReportTimes.CustomDate.Enabled = $true $ReportTimes.CustomDate.DateFrom = $DateFrom $ReportTimes.CustomDate.DateTo = $DateTo } else { return } } } [string] $ReportNameTitle = Format-AddSpaceToSentence -Text $Report if (-not $Quiet) { $Logger.AddInfoRecord("Running $ReportNameTitle report against $($Servers -join ', ')") } $Events = New-ArrayList $Dates = Get-ChoosenDates -ReportTimes $ReportTimes $MyReport = $ReportDefinitions[$Report] $LogNames = foreach ($SubReport in $MyReport.Keys | Where-Object { $_ -ne 'Enabled' }) { $MyReport[$SubReport].LogName } $LogNames = $LogNames | Sort-Object -Unique foreach ($Log in $LogNames) { $EventsID = foreach ($R in $MyReport.Values) { if ($Log -eq $R.LogName) { $R.Events if (-not $Quiet) { $Logger.AddInfoRecord("Events scanning for Events ID: $($R.Events) ($Log)") } } } foreach ($Date in $Dates) { $ExecutionTime = Start-TimeLog if (-not $Quiet) { $Logger.AddInfoRecord("Getting events for dates $($Date.DateFrom) to $($Date.DateTo)") } $FoundEvents = Get-Events -Server $Servers ` -LogName $Log ` -EventID $EventsID ` -DateFrom $Date.DateFrom ` -DateTo $Date.DateTo ` -ErrorAction SilentlyContinue ` -ErrorVariable ErrorsReturned ` -Verbose:$Verbose foreach ($MyError in $ErrorsReturned) { if (-not $Quiet) { $Logger.AddErrorRecord("Server $MyError") } } #$Logger.AddInfoRecord("Events: $EventsID Event Count: $($FoundEvents.Count)") Add-ToArrayAdvanced -List $Events -Element $FoundEvents -SkipNull -Merge $Elapsed = Stop-TimeLog -Time $ExecutionTime -Option OneLiner if (-not $Quiet) { $Logger.AddInfoRecord("Events scanned found $(Get-ObjectCount -Object $FoundEvents) - Time elapsed: $Elapsed") } } } return Get-MyEvents -Events $Events -ReportDefinition $MyReport -ReportName $Report -Quiet:$Quiet } } function Find-EventsNeeded { 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) { # Cleans up empty HostName for failed domain $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 #, # [bool] $Verbose = $false ) $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 = @() # Report Per Hour if ($ReportTimes.Contains('PastHour') -and $ReportTimes.PastHour.Enabled) { $DatesPastHour = Find-DatesPastHour if ($DatesPastHour -ne $null) { $Dates += $DatesPastHour } } if ($ReportTimes.Contains('CurrentHour') -and $ReportTimes.CurrentHour.Enabled) { $DatesCurrentHour = Find-DatesCurrentHour if ($DatesCurrentHour -ne $null) { $Dates += $DatesCurrentHour } } # Report Per Day if ($ReportTimes.Contains('PastDay') -and $ReportTimes.PastDay.Enabled) { $DatesDayPrevious = Find-DatesDayPrevious if ($DatesDayPrevious -ne $null) { $Dates += $DatesDayPrevious } } if ($ReportTimes.Contains('CurrentDay') -and $ReportTimes.CurrentDay.Enabled) { $DatesDayToday = Find-DatesDayToday if ($DatesDayToday -ne $null) { $Dates += $DatesDayToday } } # Report Per Week if ($ReportTimes.Contains('OnDay') -and $ReportTimes.OnDay.Enabled) { foreach ($Day in $ReportTimes.OnDay.Days) { $DatesReportOnDay = Find-DatesPastWeek $Day if ($DatesReportOnDay -ne $null) { $Dates += $DatesReportOnDay } } } # Report Per Month if ($ReportTimes.Contains('PastMonth') -and $ReportTimes.PastMonth.Enabled) { # Find-DatesMonthPast runs only on 1st of the month unless -Force is used $DatesMonthPrevious = Find-DatesMonthPast -Force $ReportTimes.PastMonth.Force if ($DatesMonthPrevious -ne $null) { $Dates += $DatesMonthPrevious } } if ($ReportTimes.Contains('CurrentMonth') -and $ReportTimes.CurrentMonth.Enabled) { $DatesMonthCurrent = Find-DatesMonthCurrent if ($DatesMonthCurrent -ne $null) { $Dates += $DatesMonthCurrent } } # Report Per Quarter if ($ReportTimes.Contains('PastQuarter') -and $ReportTimes.PastQuarter.Enabled) { # Find-DatesMonthPast runs only on 1st of the quarter unless -Force is used $DatesQuarterLast = Find-DatesQuarterLast -Force $ReportTimes.PastQuarter.Force if ($DatesQuarterLast -ne $null) { $Dates += $DatesQuarterLast } } if ($ReportTimes.Contains('CurrentQuarter') -and $ReportTimes.CurrentQuarter.Enabled) { $DatesQuarterCurrent = Find-DatesQuarterCurrent if ($DatesQuarterCurrent -ne $null) { $Dates += $DatesQuarterCurrent } } # Report Custom if ($ReportTimes.Contains('CurrentDayMinusDayX') -and $ReportTimes.CurrentDayMinusDayX.Enabled) { $DatesCurrentDayMinusDayX = Find-DatesCurrentDayMinusDayX $ReportTimes.CurrentDayMinusDayX.Days if ($DatesCurrentDayMinusDayX -ne $null) { $Dates += $DatesCurrentDayMinusDayX } } if ($ReportTimes.Contains('CurrentDayMinuxDaysX') -and $ReportTimes.CurrentDayMinuxDaysX.Enabled) { $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX $ReportTimes.CurrentDayMinuxDaysX.Days if ($DatesCurrentDayMinusDaysX -ne $null) { $Dates += $DatesCurrentDayMinusDaysX } } if ($ReportTimes.Contains('CustomDate') -and $ReportTimes.CustomDate.Enabled) { $DatesCustom = @{ DateFrom = $ReportTimes.CustomDate.DateFrom DateTo = $ReportTimes.CustomDate.DateTo } if ($DatesCustom -ne $null) { $Dates += $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 } $Dates += $DatesEverything } if ($ReportTimes.Contains('Last3days') -and $ReportTimes.Last3days.Enabled) { $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX -days 3 if ($DatesCurrentDayMinusDaysX -ne $null) { $Dates += $DatesCurrentDayMinusDaysX } } if ($ReportTimes.Contains('Last7days') -and $ReportTimes.Last7days.Enabled) { $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX -days 7 if ($DatesCurrentDayMinusDaysX -ne $null) { $Dates += $DatesCurrentDayMinusDaysX } } if ($ReportTimes.Contains('Last14days') -and $ReportTimes.Last14days.Enabled) { $DatesCurrentDayMinusDaysX = Find-DatesCurrentDayMinuxDaysX -days 14 if ($DatesCurrentDayMinusDaysX -ne $null) { $Dates += $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") } } #exit } } 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-EventsTranslation { [CmdletBinding()] param( [Array] $Events, [System.Collections.IDictionary] $EventsDefinition # HashTable/OrderedDictionary ) if ($EventsDefinition.Filter) { # Filter is special, if there is just one object on the right side # If there are more objects filter will pick all values on the right side and display them as required #Filter = @{ # 'ObjectClass' = 'groupPolicyContainer' # 'AttributeLDAPDisplayName' = 'cn','displayName' #} 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) { # $EventProperty.Name is value on the left side # $Fields[$EventProperty.Name] is value on the right side # $EventProperty.Value is actual value of field # Check if defined field invalidates whole Event if ($null -ne $EventsDefinition.Ignore) { if ($EventsDefinition.Ignore.Contains($EventProperty.Name)) { if ($EventProperty.Value -like $EventsDefinition.Ignore[$EventProperty.Name]) { continue } } } # Check if field requires functions 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)) { # Replaces Field with new Field according to schema $HashTable[$EventsDefinition.Fields[$EventProperty.Name]] = $EventProperty.Value } else { # This is your standard event field, we won't be using it for display most of the time # May need to be totally ignored if not needed. To be decided # Assign value - Finally (untouched one) $HashTable[$EventProperty.Name] = $EventProperty.Value } } #$HashTable | fs # This overwrites values based on parameters. It's useful for cleanup or special cases. if ($null -ne $EventsDefinition.Overwrite) { foreach ($Entry in $EventsDefinition.Overwrite.Keys) { $OverwriteObject = $EventsDefinition.Overwrite.$Entry # This allows for having multiple values in Overwrite by using additional empty spaces in definition $StrippedEntry = Remove-WhiteSpace -Text $Entry if ($OverwriteObject.Count -eq 3) { #Write-Color $Entry, ' - ', $HashTable[$Entry], ' - ', $HashTable.$Entry, ' - ', $HashTable.($OverwriteObject[0]) -Color Red #Write-Color $OverwriteObject[0], ' - ', $OverwriteObject[1], ' - ', $OverwriteObject[2] -Color Yellow 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 { # Defines Report / Dates Range dynamically from HashTables $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 { # Defines Report / Dates Range dynamically from HashTables $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 ## Only for discord $Object.AvatarName = $Notifications[$Option].AvatarName $Object.AvatarImage = $Notifications[$Option].AvatarImage # All Slack/Discord/Teams $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 { 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 ) # Get Servers $ServersList = New-ArrayList if ($Target.Servers.Enabled) { $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 } } $null = $ServersList.AddRange($Servers) } if ($Target.DomainControllers.Enabled) { $Logger.AddInfoRecord("Preparing servers list - domain controllers autodetection") [Array] $Servers = (Get-WinADDomainControllers -SkipEmpty).HostName $null = $ServersList.AddRange($Servers) } if ($Target.LocalFiles.Enabled) { $Logger.AddInfoRecord("Preparing file list - defined event logs") $Files = Get-EventLogFileList -Sections $Target.LocalFiles } # Prepare list of servers and files to scan and their relation to LogName and EventIDs and DataTimes <# Server LogName EventID Type ------ ------- ------- ---- AD1 Security {5136, 5137, 5141, 5136...} Computer AD2 Security {5136, 5137, 5141, 5136...} Computer EVO1 ForwardedEvents {5136, 5137, 5141, 5136...} Computer AD1.ad.evotec.xyz Security {5136, 5137, 5141, 5136...} Computer AD2.ad.evotec.xyz Security {5136, 5137, 5141, 5136...} Computer C:\MyEvents\Archive-Security-2018-08-21-23-49-19-424.evtx Security {5136, 5137, 5141, 5136...} File C:\MyEvents\Archive-Security-2018-09-08-02-53-53-711.evtx Security {5136, 5137, 5141, 5136...} File C:\MyEvents\Archive-Security-2018-09-14-22-13-07-710.evtx Security {5136, 5137, 5141, 5136...} File C:\MyEvents\Archive-Security-2018-09-15-09-27-52-679.evtx Security {5136, 5137, 5141, 5136...} File AD1 System 104 Computer AD2 System 104 Computer EVO1 ForwardedEvents 104 Computer AD1.ad.evotec.xyz System 104 Computer AD2.ad.evotec.xyz System 104 Computer C:\MyEvents\Archive-Security-2018-08-21-23-49-19-424.evtx System 104 File C:\MyEvents\Archive-Security-2018-09-08-02-53-53-711.evtx System 104 File C:\MyEvents\Archive-Security-2018-09-14-22-13-07-710.evtx System 104 File C:\MyEvents\Archive-Security-2018-09-15-09-27-52-679.evtx System 104 File #> # Get LogNames [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 } } } } } #$Logger.AddInfoRecord("Preparing to scan log $Log for Events:$($EventIDs -join ', ')") $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 } } 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 } } } } $OutputFiles = foreach ($File in $FIles) { [PSCustomObject]@{ Server = $File LogName = $Log EventID = $EventIDs | Sort-Object -Unique Type = 'File' DateFrom = $Dates.DateFrom DateTo = $Dates.DateTo } } $OutputServers $OutputFiles } , $ExtendedInput } function Get-ServersListLimited { [CmdletBinding()] param( [System.Collections.IDictionary] $Target, [int64] $RecordID ) $ServersList = New-ArrayList if ($Target.Servers.Enabled) { $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 } } $null = $ServersList.AddRange($Servers) } [Array] $ExtendedInput = foreach ($Server in $ServersList) { [PSCustomObject] @{ Server = $Server.ComputerName LogName = $Server.LogName RecordID = $RecordID } } , $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).") # Write-Color @script:WriteParameters '[W] ', 'Warning: ', $result.LogName, ' log on ', $result.Server, " doesn't cover whole date range requested. Oldest event ", $result.EventOldest, ' while requested ', $Dates.DateFrom, '.' -Color Blue, White, Yellow, White, Yellow, White, Yellow, White, Yellow $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 $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 ) <# <![CDATA[ <QueryList> <Query Id="0" Path="Security"> <Select Path="Security">*[System[(EventID=122 or EventID=212 or EventID=323)]]</Select> </Query> </QueryList> ]]> #> Write-Verbose "New-EventQuery - Events Count: $($Events.Count)" $values = New-ArrayList # Add-ToArray -List $Values -Element '<![CDATA[ <QueryList><Query Id="0" Path="Security">' 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>]]>' Add-ToArray -List $Values -Element ')]]</Select></Query></QueryList>' $FinalQuery = ([string] $Values) Write-Verbose $FinalQuery return ([string] $Values) #.Replace(' ', '').Replace('or', ' or ').Replace('SelectPath', 'Select Path') } function New-SubscriptionTemplates { [CmdletBinding()] param ( [alias('ReportDefinitions')][System.Collections.IDictionary] $Definitions, [alias('Servers', 'Computers')][System.Collections.IDictionary] $Target, [System.Collections.IDictionary] $LoggerParameters ) 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 ', ')") } } return $ExtendedInput return $Events = Get-EventsData -ReportDefinitions $Definitions -LogName 'Security' $Systems = Get-EventsData -ReportDefinitions $Definitions -LogName 'System' $Logger.AddInfoRecord("Found Security Events $($Events -join ', ')") $Logger.AddInfoRecord("Found System Events $($Systems -join ', ')") $ServersAD = Get-DC $Servers = Find-ServersAD -ReportDefinitions $Definitions -DC $ServersAD #Write-Color 'Found Servers ', ([string] $Servers) -Color White, Yellow $Logger.AddInfoRecord("Found Servers $($Servers -join ', ')") # $xmlTemplate = "$($($(Get-Module -ListAvailable PSWinReporting)[0]).ModuleBase)\Templates\Template-Collector.xml" $XmlTemplate = "$((get-item $PSScriptRoot).Parent.FullName)\Templates\Template-Collector.xml" if (Test-Path $xmlTemplate) { $Logger.AddInfoRecord("Found Template $xmlTemplate") #Write-Color 'Found Template ', $xmlTemplate -Color White, Yellow $ListTemplates = New-ArrayList if (Test-Path $xmlTemplate) { $Array = New-ArrayList $SplitArrayID = Split-Array -inArray $Events -size 22 # Support for more ID's then 22 (limitation of Get-WinEvent) foreach ($ID in $SplitArrayID) { $Query = New-EventQuery -Events $ID -Type 'Security' #-Verbose Add-ToArray -List $Array -Element $Query } $SplitArrayID = Split-Array -inArray $Systems -size 22 # Support for more ID's then 22 (limitation of Get-WinEvent) foreach ($ID in $SplitArrayID) { $Query = New-EventQuery -Events $ID -Type 'System' #-Verbose Add-ToArray -List $Array -Element $Query } $i = 0 foreach ($Events in $Array) { $i++ $SubscriptionTemplate = "$ENV:TEMP\PSWinReportingSubscription$i.xml" Copy-Item -Path $xmlTemplate $SubscriptionTemplate $Logger.AddInfoRecord("Copied template $SubscriptionTemplate") #Write-Color 'Copied template ', $SubscriptionTemplate -Color White, Yellow Add-ServersToXML -FilePath $SubscriptionTemplate -Servers $Servers Set-XML -FilePath $SubscriptionTemplate -Path 'Subscription' -Node 'SubscriptionId' -Value "PSWinReporting Subscription Events - $i" Set-XML -FilePath $SubscriptionTemplate -Path 'Subscription' -Node 'ContentFormat' -Value 'Events' Set-XML -FilePath $SubscriptionTemplate -Path 'Subscription' -Node 'ConfigurationMode' -Value 'Custom' #$Events Set-XML -FilePath $SubscriptionTemplate -Path 'Subscription' -Node 'Query' -Value $Events Add-ToArray -List $ListTemplates -Element $SubscriptionTemplate } } } else { $Logger.AddInfoRecord("Template not found $xmlTemplate") #Write-Color 'Template not found ', $xmlTemplate -Color White, Yellow } return $ListTemplates } function Protect-ArchivedLogs { [CmdletBinding()] param ( $TableEventLogClearedLogs, [string] $DestinationPath ) <# $EventsFound = $EventsFound | Select-Object @{label = 'Domain Controller'; expression = { $_.Computer}} , @{label = 'Action'; expression = { ($_.Message -split '\n')[0] }}, @{label = 'Backup Path'; expression = { if ($_.BackupPath -eq $null) { 'N/A' } else { $_.BackupPath} }}, @{label = 'Log Type'; expression = { if ($Type -eq 'Security') { 'Security' } else { $_.Channel } }}, @{label = 'Who'; expression = { if ($_.ID -eq 1105) { "Automatic Backup" } else { "$($_.SubjectDomainName)\$($_.SubjectUserName)" }}}, @{label = 'When'; expression = { $_.Date }}, @{label = 'Event ID'; expression = { $_.ID }}, @{label = 'Record ID'; expression = { $_.RecordId }} | Sort-Object When $EventsFound = Find-EventsIgnored -Events $EventsFound -IgnoreWords $IgnoreWords #> 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 ) $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") #Write-Color 'Deleting own providers - ', $Subscription -Color White, Green Start-MyProgram -Program $Script:ProgramWecutil -cmdArgList 'ds', $Subscription } if ($All -eq $true -and $Subscription -notlike '*PSWinReporting*') { $Logger.AddInfoRecord("Deleting own providers - $Subscription") #Write-Color 'Deleting all providers - ', $Subscription -Color White, Green Start-MyProgram -Program $Script:ProgramWecutil -cmdArgList 'ds', $Subscription } } } function Remove-TaskScheduledForwarder { [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' ## Define reports $Script:ReportDefinitions = [ordered] @{ UserChanges = @{ Enabled = $true SqlExport = @{ EnabledGlobal = $false Enabled = $false SqlServer = 'EVO1' SqlDatabase = 'SSAE18' SqlTable = 'dbo.[EventsNewSpecial]' # Left side is data in PSWinReporting. Right Side is ColumnName in SQL # Changing makes sense only for right side... SqlTableCreate = $true SqlTableAlterIfNeeded = $false # if table mapping is defined doesn't do anything SqlCheckBeforeInsert = 'EventRecordID', 'DomainController' # Based on column name 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' # ColumnsToTrack when it was added to database and by who / not part of event 'AddedWho' = 'EventAddedWho' # ColumnsToTrack when it was added to database and by who / not part of event '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' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } Ignore = @{ # Cleanup Anonymous LOGON (usually related to password events) # https://social.technet.microsoft.com/Forums/en-US/5b2a93f7-7101-43c1-ab53-3a51b2e05693/eventid-4738-user-account-was-changed-by-anonymous?forum=winserverDS SubjectUserName = "ANONYMOUS LOGON" # Test value #ProfilePath = 'C*' } Functions = @{ 'ProfilePath' = 'Convert-UAC' 'OldUacValue' = 'Remove-WhiteSpace', 'Convert-UAC' 'NewUacValue' = 'Remove-WhiteSpace', 'Convert-UAC' 'UserAccountControl' = 'Remove-WhiteSpace', 'Split-OnSpace', 'Convert-UAC' } IgnoreWords = @{ #'Profile Path' = 'TEMP*' } SortBy = 'When' } } UserChangesDetailed = [ordered] @{ Enabled = $true Events = @{ Enabled = $true Events = 5136, 5137, 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' # Common Fields 'RecordID' = 'Record ID' 'ID' = 'Event ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'Record ID' Descending = $false IgnoreWords = @{ } } } ComputerChangesDetailed = [ordered] @{ Enabled = $true Events = @{ Enabled = $true Events = 5136, 5137, 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' # Common Fields 'RecordID' = 'Record ID' 'ID' = 'Event ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'Record ID' Descending = $false IgnoreWords = @{} } } UserStatus = @{ Enabled = $true 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' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'When' } } UserLockouts = @{ Enabled = $true 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' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'When' } } UserLogon = @{ Enabled = $false Events = @{ Enabled = $false 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' # %%1833 = Impersonation 'VirtualAccount' = 'VirtualAccount' # %%1843 = No 'ElevatedToken' = 'ElevatedToken' # %%1842 = Yes 'LogonType' = 'LogonType' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } IgnoreWords = @{} } } UserUnlocked = @{ # 4767 A user account was unlocked # https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=4767 Enabled = $true 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' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'When' } } ComputerCreatedChanged = @{ Enabled = $true Events = @{ Enabled = $true Events = 4741, 4742 # created, changed LogName = 'Security' Ignore = @{ # Cleanup Anonymous LOGON (usually related to password events) # https://social.technet.microsoft.com/Forums/en-US/5b2a93f7-7101-43c1-ab53-3a51b2e05693/eventid-4738-user-account-was-changed-by-anonymous?forum=winserverDS 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' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } IgnoreWords = @{} } } ComputerDeleted = @{ Enabled = $true Events = @{ Enabled = $true Events = 4743 # deleted LogName = 'Security' IgnoreWords = @{} Fields = [ordered] @{ 'Computer' = 'Domain Controller' 'Action' = 'Action' 'ObjectAffected' = 'Computer Affected' 'Who' = 'Who' 'Date' = 'When' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'When' } } UserLogonKerberos = @{ Enabled = $false Events = @{ Enabled = $false 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' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'When' } } GroupMembershipChanges = @{ Enabled = $true Events = @{ Enabled = $true Events = 4728, 4729, 4732, 4733, 4746, 4747, 4751, 4752, 4756, 4757, 4761, 4762, 4785, 4786, 4787, 4788 LogName = 'Security' IgnoreWords = @{ #'Who' = '*ANONYMOUS*' } Fields = [ordered] @{ 'Computer' = 'Domain Controller' 'Action' = 'Action' 'TargetUserName' = 'Group Name' 'MemberNameWithoutCN' = 'Member Name' 'Who' = 'Who' 'Date' = 'When' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'When' } } GroupEnumeration = @{ Enabled = $false Events = @{ Enabled = $true Events = 4798, 4799 LogName = 'Security' IgnoreWords = @{ #'Who' = '*ANONYMOUS*' } Fields = [ordered] @{ 'Computer' = 'Domain Controller' 'Action' = 'Action' 'TargetUserName' = 'Group Name' 'Who' = 'Who' 'Date' = 'When' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'When' } } GroupChanges = @{ Enabled = $true 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' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'When' } } GroupCreateDelete = @{ Enabled = $true Events = @{ Enabled = $true Events = 4727, 4730, 4731, 4734, 4744, 4748, 4749, 4753, 4754, 4758, 4759, 4763 LogName = 'Security' IgnoreWords = @{ # 'Who' = '*ANONYMOUS*' } Fields = [ordered] @{ 'Computer' = 'Domain Controller' 'Action' = 'Action' 'TargetUserName' = 'Group Name' 'Who' = 'Who' 'Date' = 'When' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'When' } } GroupChangesDetailed = [ordered] @{ Enabled = $true Events = @{ Enabled = $true Events = 5136, 5137, 5141 LogName = 'Security' Filter = @{ # Filter is special # if there is just one object on the right side it will filter on that field # if there are more objects filter will pick all values on the right side and display them (using AND) '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' # Common Fields 'RecordID' = 'Record ID' 'ID' = 'Event ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'Record ID' Descending = $false IgnoreWords = @{ } } } GroupPolicyChanges = [ordered] @{ Enabled = $true 'Group Policy Name Changes' = @{ Enabled = $true Events = 5136, 5137, 5141 LogName = 'Security' Filter = @{ # Filter is special, if there is just one object on the right side # If there are more objects filter will pick all values on the right side and display them as required 'ObjectClass' = 'groupPolicyContainer' #'OperationType' = 'Value Added' 'AttributeLDAPDisplayName' = $null, 'displayName' #, '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' #'AttributeSyntaxOID' = 'AttributeSyntaxOID' 'AttributeValue' = 'AttributeValue' 'OperationType' = 'OperationType' 'OpCorrelationID' = 'OperationCorelationID' 'AppCorrelationID' = 'OperationApplicationCorrelationID' 'DSName' = 'DSName' 'DSType' = 'DSType' 'Task' = 'Task' 'Version' = 'Version' # Common Fields '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 = @{ # Filter is special, if there is just one object on the right side # If there are more objects filter will pick all values on the right side and display them as required 'ObjectClass' = 'groupPolicyContainer' #'OperationType' = 'Value Added' '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' #'AttributeSyntaxOID' = 'AttributeSyntaxOID' 'AttributeValue' = 'AttributeValue' 'OperationType' = 'OperationType' 'OpCorrelationID' = 'OperationCorelationID' 'AppCorrelationID' = 'OperationApplicationCorrelationID' 'DSName' = 'DSName' 'DSType' = 'DSType' 'Task' = 'Task' 'Version' = 'Version' # Common Fields '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 = @{ # Filter is special, if there is just one object on the right side # If there are more objects filter will pick all values on the right side and display them as required 'ObjectClass' = 'domainDNS' #'OperationType' = 'Value Added' #'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' #'AttributeSyntaxOID' = 'AttributeSyntaxOID' 'AttributeValue' = 'AttributeValue' 'OperationType' = 'OperationType' 'OpCorrelationID' = 'OperationCorelationID' 'AppCorrelationID' = 'OperationApplicationCorrelationID' 'DSName' = 'DSName' 'DSType' = 'DSType' 'Task' = 'Task' 'Version' = 'Version' # Common Fields 'ID' = 'Event ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' } SortBy = 'Record ID' Descending = $false IgnoreWords = @{ } } } LogsClearedSecurity = @{ Enabled = $true 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' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' 'GatheredLogName' = 'Gathered LogName' #'Test' = 'Test' } SortBy = 'When' IgnoreWords = @{} Overwrite = @{ # Allows to overwrite field content on the fly, either only on IF or IF ELSE # IF <VALUE> -eq <VALUE> THEN <VALUE> (3 VALUES) # IF <VALUE> -eq <VALUE> THEN <VALUE> ELSE <VALUE> (4 VALUES) # If you need to use IF multiple times for same field use spaces to distinguish HashTable Key. 'Backup Path' = 'Backup Path', '', 'N/A' #'Backup Path ' = 'Backup Path', 'C:\Windows\System32\Winevt\Logs\Archive-Security-2018-11-24-09-25-36-988.evtx', 'MMMM' 'Who' = 'Event ID', 1105, 'Automatic Backup' # if event id 1105 set field to Automatic Backup #'Test' = 'Event ID', 1106, 'Test', 'Mama mia' } } } LogsClearedOther = @{ Enabled = $true 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' # Common Fields 'ID' = 'Event ID' 'RecordID' = 'Record ID' 'GatheredFrom' = 'Gathered From' } SortBy = 'When' Overwrite = @{ # Allows to overwrite field content on the fly, either only on IF or IF ELSE # IF <VALUE> -eq <VALUE> THEN <VALUE> (3 VALUES) # IF <VALUE> -eq <VALUE> THEN <VALUE> ELSE <VALUE> (4 VALUES) # If you need to use IF multiple times for same field use spaces to distinguish HashTable Key. 'Backup Path' = 'Backup Path', '', 'N/A' } } } EventsReboots = @{ Enabled = $false Events = @{ Enabled = $true Events = 1001, 1018, 1, 12, 13, 42, 41, 109, 1, 6005, 6006, 6008, 6013 LogName = 'System' IgnoreWords = @{ } } } } $Script:ReportTimes = @{ # Report Per Hour PastHour = @{ Enabled = $false # if it's 23:22 it will report 22:00 till 23:00 } CurrentHour = @{ Enabled = $false # if it's 23:22 it will report 23:00 till 00:00 } # Report Per Day PastDay = @{ Enabled = $false # if it's 1.04.2018 it will report 31.03.2018 00:00:00 till 01.04.2018 00:00:00 } CurrentDay = @{ Enabled = $false # if it's 1.04.2018 05:22 it will report 1.04.2018 00:00:00 till 01.04.2018 00:00:00 } # Report Per Week OnDay = @{ Enabled = $false Days = 'Monday'#, 'Tuesday' } # Report Per Month PastMonth = @{ Enabled = $false # checks for 1st day of the month - won't run on any other day unless used force Force = $true # if true - runs always ... } CurrentMonth = @{ Enabled = $false } # Report Per Quarter PastQuarter = @{ Enabled = $false # checks for 1st day fo the quarter - won't run on any other day Force = $true } CurrentQuarter = @{ Enabled = $false } # Report Custom CurrentDayMinusDayX = @{ Enabled = $false Days = 7 # goes back X days and shows just 1 day } CurrentDayMinuxDaysX = @{ Enabled = $false Days = 3 # goes back X days and shows X number of days till Today } 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() # Building message $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 } } } # Slack Notifications 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 } # Microsoft Teams Nofications 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 # -Verbose Write-Color @script:WriteParameters -Text "[i] Teams output: ", $Data -Color White, Yellow } # Discord Notifications 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) { # Prepare email body $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 $HtmlBody += Export-ReportToHTML -Report $true-ReportTable $Events -ReportTableText "Quick notification event" # Do Cleanup of Emails #$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-ReportFileName -ReportOptions $Options -ReportExtension 'html' $ReportHTMLPath = Set-ReportFile -Path $Env:TEMP -FileNamePattern 'PSWinReporting.html' -DateFormat $null try { $HTML | Out-File -Encoding Unicode -FilePath $ReportHTMLPath -ErrorAction Stop $Logger.AddInfoRecord("Saving report to file: $ReportHTMLPath") if ($Options.SendMail.Attach.HTML) { $AttachHTML += $ReportHTMLPath $AttachedReports += $ReportHTMLPath } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Logger.AddErrorRecord("Error saving file $ReportHTMLPath.") $Logger.AddErrorRecord("Error: $ErrorMessage") } $TemporarySubject = $Options.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 # HTML Report settings $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 { 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 } foreach ($TemplatePath in $ListTemplates) { #Write-Color 'Adding provider ', $TemplatePath, ' to Subscriptions.' -Color White, Green, White $Logger.AddInfoRecord("Adding provider $TemplatePath to Subscriptions.") Start-MyProgram -Program $Script:ProgramWecutil -cmdArgList 'cs', $TemplatePath } } function Set-TimeReports { [CmdletBinding()] param( [System.Collections.IDictionary] $HashTable ) # Get all report Names $Reports = @() foreach ($reportName in $($HashTable.GetEnumerator().Name)) { $Reports += $reportName } # Get Highest Count of servers $Count = 0 foreach ($reportName in $reports) { if ($($HashTable[$reportName]).Count -ge $Count) { $Count = $($HashTable[$reportName]).Count } } $Count = $Count - 1 # Removes Total from Server Count $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-ADReporting () { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [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 ) [bool] $WarningNoLogger = $false <# Set logger #> if (-not $LoggerParameters) { $LoggerParameters = $Script:LoggerParameters $WarningNoLogger = $true } $Params = @{ LogPath = if ([string]::IsNullOrWhiteSpace($LoggerParameters.LogsDir)) { '' } else { Join-Path $LoggerParameters.LogsDir "$([datetime]::Now.ToString('yyyy.MM.dd_hh.mm'))_ADReporting.log" } ShowTime = $LoggerParameters.ShowTime TimeFormat = $LoggerParameters.TimeFormat } $Logger = Get-Logger @Params if ($WarningNoLogger) { $Logger.AddWarningRecord("New version of PSWinReporting requires Logger Parameter. Please read documentation. No logs will be written to disk.") } <# Test Configuration #> $Params = @{ LoggerParameters = $LoggerParameters EmailParameters = $EmailParameters FormattingParameters = $FormattingParameters ReportOptions = $ReportOptions ReportTimes = $ReportTimes ReportDefinitions = $ReportDefinitions } if (-not (Test-Configuration @Params)) { $Logger.AddErrorRecord("There are parameters missing in configuration file. Can't continue running.") exit } <# Test Modules #> if (-not (Test-Modules -ReportOptions $ReportOptions)) { $Logger.AddErrorRecord("Install the necessary modules. Can't continue running.") } if ($ReportOptions.JustTestPrerequisite) { exit } <# Test AD availability #> try { $null = Get-ADDomain } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Logger.AddErrorRecord("Failed to get AD domain information: $ErrorMessage)") exit } $Logger.AddInfoRecord('Starting to build a report') $Dates = Get-ChoosenDates -ReportTimes $ReportTimes foreach ($Date in $Dates) { Start-Report -Dates $Date -EmailParameters $EmailParameters -FormattingParameters $FormattingParameters -ReportOptions $ReportOptions -ReportDefinitions $ReportDefinitions } } function Start-Notifications { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [System.Collections.IDictionary] $Definitions, [System.Collections.IDictionary] $Target, [int] $EventID, [int64] $EventRecordID, [string] $EventChannel ) # Logger Setup 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) { # Terminating as no options are $true 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 # Prepare the results based on chosen criteria foreach ($Report in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled' }) { if ($Definitions.$Report.Enabled) { #$ReportNameTitle = Format-AddSpaceToSentence -Text $Report -ToLowerCase $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-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() # Timer Start # Declare variables $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' # Summary of events to process $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) { #$Events += Get-Events -Server $ReportDefinitions.ReportsAD.ForwardServer -LogName $ReportDefinitions.ReportsAD.ForwardServer.ForwardEventLog $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") } # Process events $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 } } # Prepare email body $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 # prepare body with HTML 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') # Do Cleanup of Emails $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() # Sending email - finalizing package 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} #-Verbose } else { $SendMail = Send-Email -EmailParameters $EmailParameters -Body $EmailBody -Attachment $Reports -Subject $TemporarySubject #-Verbose } 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 $Results = @{} $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 ', ')") } } # Scan all events and get everything at once $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") } # Prepare the results based on chosen criteria foreach ($Report in $Definitions.Keys | Where-Object { $_ -notcontains 'Enabled' }) { if ($Definitions.$Report.Enabled) { #$ReportNameTitle = Format-AddSpaceToSentence -Text $Report -ToLowerCase $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") } } # Prepare email body - tables (real data) if ($Options.AsHTML.Enabled) { # Prepare email body $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 #$EmailBody += Export-ReportToHTML -Report $Definitions.ReportsAD.Custom.ServersData.Enabled -ReportTable $ServersAD -ReportTableText 'Following AD servers were detected in forest' #$EmailBody += Export-ReportToHTML -Report $Definitions.ReportsAD.Custom.FilesData.Enabled -ReportTable $TableEventLogFiles -ReportTableText 'Following files have been processed for events' #$EmailBody += Export-ReportToHTML -Report $Definitions.ReportsAD.Custom.EventLogSize.Enabled -ReportTable $EventLogTable -ReportTableText 'Following event log sizes were reported' 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" } # Do Cleanup of Emails $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-ReportFileName -ReportOptions $Options -ReportExtension 'html' $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 ` -HideLogos:(-not $Options.AsDynamicHTML.Branding.Logo.Show) ` -RightLogoString $Options.AsDynamicHTML.Branding.Logo.RightLogo.ImageLink ` -UseCssLinks:$Options.AsDynamicHTML.EmbedCSS ` -UseStyleLinks:$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 # $ReportFilePathXLSX = Set-ReportFileName -ReportOptions $Options -ReportExtension "xlsx" #Export-ReportToXLSX -Report $Definitions.ReportsAD.Custom.ServersData.Enabled -ReportOptions $Options -ReportFilePath $ReportFilePathXLSX -ReportName "Processed Servers" -ReportTable $ServersAD #Export-ReportToXLSX -Report $Definitions.ReportsAD.Custom.EventLogSize.Enabled -ReportOptions $Options -ReportFilePath $ReportFilePathXLSX -ReportName "Event log sizes" -ReportTable $EventLogTable 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') #$ReportFilePathCSV += Export-ReportToCSV -Report $Definitions.ReportsAD.Custom.ServersData.Enabled -ReportOptions $Options -Extension "csv" -ReportName "ReportServers" -ReportTable $ServersAD #$ReportFilePathCSV += Export-ReportToCSV -Report $Definitions.ReportsAD.Custom.EventLogSize.Enabled -ReportOptions $Options -Extension "csv" -ReportName "ReportEventLogSize" -ReportTable $EventLogTable 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 # Sending email - finalizing package 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.AddInfoRecord("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-SubscriptionService { [CmdletBinding()] param( [System.Collections.IDictionary] $LoggerParameters ) if (-not $LoggerParameters) { $LoggerParameters = $Script:LoggerParameters } $Logger = Get-Logger @LoggerParameters $Logger.AddInfoRecord('Starting Windows Event Collector service.') #Write-Color 'Starting Windows Event Collector service.' -Color White, Green, White $Output = Start-MyProgram -Program $Script:ProgramWecutil -cmdArgList 'qc', '/q:true' $Logger.AddInfoRecord($Output) } 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 ) # Logger Setup if ($Options.Logging) { $LoggerParameters = $Options.Logging } else { $LoggerParameters = $Script:LoggerParameters } $Logger = Get-Logger @LoggerParameters # Test Configuration #TODO # Test Modules #TODO # Run report $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 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.') # Configuration successful check flag $Success = $true #region EmailParameters $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 #endregion EmailParameters #region EmailParameters $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 #endregion EmailParameters #region FormattingParameters $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 #endregion FormattingParameters #region ReportOptions $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 $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 <# This is not required to exists. Only if SQL is needed, used. $Success = (Test-Key $ReportOptions.AsSql "ReportOptions.AsSql" "SqlTableMapping" -DisplayProgress) -and $Success if (Test-Key $ReportOptions.AsSql "ReportOptions.AsSql" "SqlTableMapping" ) { $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Event ID" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Who" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "When" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Record ID" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Domain Controller" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Action" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Group Name" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "User Affected" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Member Name" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Computer Lockout On" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Reported By" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "SamAccountName" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Display Name" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "UserPrincipalName" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Home Directory" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Home Path" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Script Path" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Profile Path" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "User Workstation" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Password Last Set" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Account Expires" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Primary Group Id" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Allowed To Delegate To" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Old Uac Value" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "New Uac Value" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "User Account Control" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "User Parameters" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Sid History" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Logon Hours" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "OperationType" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Message" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Backup Path" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Log Type" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "AddedWhen" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "AddedWho" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Gathered From" -DisplayProgress) -and $Success $Success = (Test-Key $ReportOptions.AsSql.SqlTableMapping "ReportOptions.SqlTableMapping" "Gathered LogName" -DisplayProgress) -and $Success } #> } } #endregion ReportOptions #region Report Definions $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 } #endregion Report Definions #region ReportOptions Per Hour $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 } #endregion ReportTimes Per Hour #region ReportTimes Per Day $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 } #endregion ReportTimes Per Day #region ReportTimes Per Month $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 #endregion ReportTimes Per Month #region ReportTimes Per Quarter $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 #endregion ReportTimes Per Quarter #region ReportTimes Custom Dates $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 } #endregion ReportTimes Custom Dates #region ReportOptions Options #$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 #$Success = (Test-Key $ReportOptions "ReportOptions" "SendMail" -DisplayProgress) -and $Success #$Success = (Test-Key $ReportOptions "ReportOptions" "KeepReportsPath" -DisplayProgress) -and $Success #$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 #endregion ReportOptions Options 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 } } #Write-Verbose "Test-Key - Continuing 1" if (-not $PSBoundParameters.ContainsKey('ValueType')) { if ($DisplayProgress) { $Logger.AddSuccessRecord("Parameter $ConfigurationKey in configuration of $ConfigurationSection exists") } return $true } #Write-Verbose "Test-Key - Continuing 2" 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 } #Write-Verbose "Test-Key - Continuing 3" 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-TaskScheduledForwarder','Find-Events','New-SubscriptionTemplates','Remove-TaskScheduledForwarder','Set-SubscriptionTemplates','Start-ADReporting','Start-Notifications','Start-SubscriptionService','Start-WinReporting') ` -Alias @() |