Scroll Top
Evotec Services sp. z o.o., ul. Drozdów 6, Mikołów, 43-190, Poland

PSWinReporting – PowerShell Module

This PowerShell Module, which started as an event library (Get-EventsLibrary.ps1), has now grown up and became full fledged PowerShell Module. This module has multiple functionalities but one of the signature features of this module is ability to parse Security (mostly) logs on Domain Controllers. But that's not all. You can set up reporting on it and have emails delivered with summary of hourly, daily, weekly, monthly or quarterly changes. Changes that happen on your Active Directory Domain. Changes that your Service Desk agents, or other administrators do. And with new versions… well you can do a lot of stuff. Just read below. Make sure to go thru related articles as they have all the KNOW HOW which is quite useful if you want to get everything from this module.

Active Directory events it can track
  • Group create, delete, modify (Who / When / What)
  • Group membership changes (Who / When / What)
  • User changes (Who / When / What)
  • User created / deleted (Who / When)
  • User password changes (Who / When)
  • User lockouts (Who / When / Where)
  • Computer Created / Modified (Who / When / Where)
  • Computer Deleted (Who / When / Where)
  • Event Log Backup (Who / When)
  • Event Log Clear (Who / When)
New features
  • Support for Event Forwarding – monitoring one event log instead of scanning all domain controllers
  • Support for Microsoft Teams – Sending events as they happen to Microsoft Teams (only supported when forwarders are in use)
  • Support for Slack – Sending events as they happen to Slack (only supported when forwarders are in use)
  • Support for Microsoft SQL – Sending events directly to SQL (some people prefer it that way)
  • Support for backing up old archived logs (moves logs from Domain Controllers into chosen place)
  • Support for re-scanning logs from files – a way to recheck your logs for missing information
Note worthy features
Provides easy to use and free monitoring of Windows Events
Allows you to monitor who and when did changes to AD
Code uses Runspaces allowing you to run code in parallel
Much faster then Get-EventsLibrary.ps1
Can be run daily, weekly, monthly or on demand
Useful links
Code is published on GitHub
Issues should be reported on GitHub
Code is published as a module on PowerShellGallery
But what is it actually?

You can talk what you want … but what is it actually? Well… one picture is worth a lot of words.

Well… that's not much. I know it's empty… but if there are actions.. you actually get things displayed.

What else? How about sending to Slack?

PSWinReporting Send To Slack

What else? How about sending to Microsoft Teams?

What else? How about sending to MS SQL?

Related articles
Last weeks of PSWinReporting Legacy Edition – summary of Legacy Features and configuration files and an announcement what to expect from New Hope
Review of new features in PSWinReporting 1.7 – Lots of changes, review required. Microsoft Teams, Slack, SQL and forwarders support
Review of new features in PSWinReporting 1.0 – Lots of changes, review required.
Last version of Get-EventsLibrary.ps1 – This is actual code base for the old version. Just in case…
Blog post about version 0.8 – Updates from feedback. Last version before name change.
Blog post about version 0.7 – Updates from feedback.
Blog post about version 0.6 – Updates from feedback.
Blog post about initial version and the differences in monitoring approach
To Do
Support for forwarded events
Support for encrypting email password
Active Directory Diagnostics Reporting
File Server Events monitoring
Required prerequisites

Before you are able to use this script you need to do few manual steps. Since this script is published as module… it's quite easy to set this up. Just execute commands below and use the below configuration file…

Install-Module PSWinReporting
Install-Module PSWriteColor
Install-Module PSEventViewer
Install-Module PSWriteExcel
Install-Module PSSharedGoods

When you want to update this module… just use

Update-Module PSWinReporting
Update-Module PSWriteColor
Update-Module PSEventViewer
Update-Module PSWriteExcel
Update-Module PSSharedGoods

And if you want to use advanced functionality of this module (export to SQL, export to MS Teams, Slack) you need following modules

Install-Module PSTeams
Install-Module PSSlack # not my module - big thanks to psCookieMonster
Install-Module dbaTools # not my module - big thanks to whole DBATools community

When you want to update this module… just use

Update-Module PSTeams
Update-Module PSSlack # not my module - big thanks to psCookieMonster
Update-Module dbaTools # not my module - big thanks to whole DBATools community

While this module is PSWinReporting I've decided to split out some parts of Get-EventsLibrary.ps1 (former name) into separate functions/modules. That's why there are more modules to install. You can of course install everything manually from GitHub (as everything is published there) but it will be far easier to just use Install-Module.

How do I get this running?

Essentially to run this module you just have to put following code into file, change settings to fit your needs (Email Parameters, Branding, Report Options and Report Times) and run it. That's it! After you're happy with results just Schedule it and enjoy free Windows Active Directory monitoring. Of course remember to Install prerequisites.

You should also consider reading related articles above. They contain useful information, features description and some know how.

$EmailParameters = @{
    EmailFrom                   = "notifications@domain.com"
    EmailTo                     = "przemyslaw.klys@domain.com, admin@domain.com"
    EmailCC                     = ""
    EmailBCC                    = ""
    EmailReplyTo                = ""
    EmailServer                 = "smtp.office365.com"
    EmailServerPassword         = "YourPassword"
    EmailServerPasswordAsSecure = $false
    EmailServerPasswordFromFile = $false
    EmailServerPort             = "587"
    EmailServerLogin            = "notifications@domain.com"
    EmailServerEnableSSL        = 1
    EmailEncoding               = "Unicode"
    EmailSubject                = "[Reporting] Event Changes for period <<DateFrom>> to <<DateTo>>"
    EmailPriority               = "Low" # Normal, High
}
$FormattingParameters = @{
    CompanyBranding        = @{
        Logo   = 'https://evotec.xyz/wp-content/uploads/2015/05/Logo-evotec-012.png'
        Width  = '200'
        Height = ''
        Link   = 'https://evotec.xyz'
        Inline = $false
    }
    FontFamily             = 'Calibri Light'
    FontSize               = '9pt'
    FontHeadingFamily      = 'Calibri Light'
    FontHeadingSize        = '12pt'

    FontTableHeadingFamily = 'Calibri Light'
    FontTableHeadingSize   = '9pt'

    FontTableDataFamily    = 'Calibri Light'
    FontTableDataSize      = '9pt'

    Colors                 = @{
        # case sensitive
        Red   = 'removed', 'deleted', 'locked out', 'lockouts', 'disabled', 'Domain Admins', 'was cleared'
        Blue  = 'changed', 'changes', 'change', 'reset'
        Green = 'added', 'enabled', 'unlocked', 'created'
    }
    Styles                 = @{
        # case sensitive
        B = 'status', 'Domain Admins', 'Enterprise Admins', 'Schema Admins', 'was cleared', 'lockouts' # BOLD
        I = '' # Italian
        U = 'status'# Underline
    }
    Links                  = @{

    }
}
$ReportOptions = @{
    JustTestPrerequisite  = $false # runs testing without actually running script

    AsExcel               = $false # attaches Excel to email with all events, required ImportExcel module
    AsCSV                 = $false # attaches CSV to email with all events,
    AsHTML                = $true # puts exported data into email directly with all events
    SendMail              = $false
    OpenAsFile            = $true
    KeepReports           = $true # keeps files after reports are sent (only if AssExcel/AsCSV are in use)
    KeepReportsPath       = "C:\Support\Reports\ExportedEvents" # if empty, temp path is used
    FilePattern           = "Evotec-<currentdate>.<extension>"
    FilePatternDateFormat = "yyyy-MM-dd-HH_mm_ss"
    RemoveDuplicates      = $true #

    AsSql                 = @{
        Use                   = $true
        SqlServer             = 'EVOWIN'
        SqlDatabase           = 'SSAE18'
        SqlTable              = 'dbo.[Events]'
        # 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,[datetime]'
            'Account Expires'        = 'AccountExpires,[datetime]'
            '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'
        }
    }


    DisplayConsole        = @{
        ShowTime   = $true
        LogFile    = "$Env:USERPROFILE\Desktop\PSWinReporting-Manual.log"
        TimeFormat = "yyyy-MM-dd HH:mm:ss"
    }
    Debug                 = @{
        DisplayTemplateHTML = $false
        Verbose             = $false
    }
}
$ReportTimes = @{
    # Report Per Hour
    PastHour             = $false # if it's 23:22 it will report 22:00 till 23:00
    CurrentHour          = $false # if it's 23:22 it will report 23:00 till 00:00
    # Report Per Day
    PastDay              = $false # if it's 1.04.2018 it will report 31.03.2018 00:00:00 till 01.04.2018 00:00:00
    CurrentDay           = $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   = $false  # if true - runs always ...
    }
    CurrentMonth         = $true

    # Report Per Quarter
    PastQuarter          = @{
        Enabled = $false # checks for 1st day fo the quarter - won't run on any other day
        Force   = $false
    }
    CurrentQuarter       = $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
    }
    Everything           = $false
}
$ReportDefinitions = @{
    TimeToGenerate = $false

    ReportsAD      = @{
        Servers           = @{
            UseForwarders   = $true # if $true skips Automatic/OnlyPDC/DC for reading logs. However it uses Automatic to deliver size of logs so keep Automatic to $true
            ForwardServer   = $ENV:COMPUTERNAME
            ForwardEventLog = 'ForwardedEvents'

            UseDirectScan   = $true
            Automatic       = $true
            OnlyPDC         = $false
            DC              = ''
        }
        ArchiveProcessing = @{
            Use         = $false
            Directories = [ordered] @{
                Use      = $false
                MyEvents = 'E:\EventLogs' #
                #MyOtherEvent = 'C:\MyEvent1'
            }
            Files       = [ordered] @{
                Use = $false
                #File1 = 'E:\EventLogs\Archive-Security-2018-09-14-22-13-07-710.evtx'
            }
        }
        EventBased        = @{
            UserChanges            = @{
                Enabled          = $true
                EnabledSqlGlobal = $true
                Events           = 4720, 4738
                LogName          = 'Security'
                IgnoreWords      = @{}
            }
            UserStatus             = @{
                Enabled          = $true
                EnabledSqlGlobal = $true
                Events           = 4722, 4725, 4767, 4723, 4724, 4726
                LogName          = 'Security'
                IgnoreWords      = @{}
                ExportToSql      = @{
                    Use                   = $true
                    SqlServer             = 'EVOWIN'
                    SqlDatabase           = 'SSAE18'
                    SqlTable              = 'dbo.[EventsUserStatus]'
                    # 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,[datetime]'
                        'Account Expires'        = 'AccountExpires,[datetime]'
                        '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'
                    }
                }
            }
            UserLockouts           = @{
                Enabled          = $true
                EnabledSqlGlobal = $true
                Events           = 4740
                LogName          = 'Security'
                IgnoreWords      = @{}
            }
            UserLogon              = @{
                Enabled          = $false
                EnabledSqlGlobal = $true
                Events           = 4624
                LogName          = 'Security'
                IgnoreWords      = @{}
            }
            GroupMembershipChanges = @{
                Enabled          = $true
                EnabledSqlGlobal = $true
                Events           = 4728, 4729, 4732, 4733, 4756, 4757, 4761, 4762
                LogName          = 'Security'
                IgnoreWords      = @{}
            }
            GroupCreateDelete      = @{
                Enabled          = $true
                EnabledSqlGlobal = $true
                Events           = 4727, 4730, 4731, 4734, 4759, 4760, 4754, 4758
                LogName          = 'Security'
                IgnoreWords      = @{}
            }
            GroupPolicyChanges     = @{
                Enabled          = $false
                EnabledSqlGlobal = $true
                Events           = 5136, 5137, 5141
                LogName          = 'Security'
                IgnoreWords      = @{}
            }
            LogsClearedSecurity    = @{
                Enabled          = $true
                EnabledSqlGlobal = $true
                Events           = 1102
                LogName          = 'Security'
                IgnoreWords      = @{}
                ExportToSql      = @{
                    Use                   = $false
                    SqlServer             = 'EVO1'
                    SqlDatabase           = 'SSAE18'
                    SqlTable              = 'dbo.[EventsLogsClearedSecurity]'
                    SqlTableCreate        = $true
                    SqlTableAlterIfNeeded = $false # if table mapping is defined doesn't do anything
                    SqlCheckBeforeInsert  = 'EventRecordID', 'DomainController' # Based on column nameg
                }
            }
            LogsClearedOther       = @{
                Enabled          = $true
                EnabledSqlGlobal = $true
                Events           = 104
                LogName          = 'System' # Source: EventLog, Task: 'Log clear'
                IgnoreWords      = @{}
            }
            EventsReboots          = @{
                Enabled          = $false
                EnabledSqlGlobal = $true
                Events           = 1001, 1018, 1, 12, 13, 42, 41, 109, 1, 6005, 6006, 6008, 6013
                LogName          = 'System'
                IgnoreWords      = @{}
            }
            ComputerCreatedChanged = @{
                Enabled          = $true
                EnabledSqlGlobal = $true
                Events           = 4741, 4742 # created, changed
                LogName          = 'Security'
                IgnoreWords      = @{}
            }
            ComputerDeleted        = @{
                Enabled          = $true
                EnabledSqlGlobal = $true
                Events           = 4743 # deleted
                LogName          = 'Security'
                IgnoreWords      = @{}
            }
        }
        Custom            = @{
            EventLogSize = @{
                Enabled          = $true
                EnabledSqlGlobal = $false
                Logs             = 'Security', 'Application', 'System'
                SortBy           = ''
            }
            ServersData  = @{
                Enabled          = $true
                EnabledSqlGlobal = $false
            }
            FilesData    = @{
                Enabled = $true
            }
        }
    }
}

Import-Module PSWinReporting -Force

### Starts Module (Requires config above)
$startADReportingSplat = @{
    ReportDefinitions    = $ReportDefinitions
    ReportTimes          = $ReportTimes
    FormattingParameters = $FormattingParameters
    ReportOptions        = $ReportOptions
    EmailParameters      = $EmailParameters
}
Start-ADReporting @startADReportingSplat