Powershell audits Active Directory with change alerts. Part 1

I will begin a series of publications on monitoring Active Directory.
In these articles I will give the most basic problems and ways to solve them. Based on these data, the functionality is easily expanded to the requirements that you need.
Given that Powershell is available for all operating systems starting with Windows Server 2003 R2 and Windows XP SP3 now. I think that this article will be a useful help, because does not require the administrator to introduce any additional funds, i.e. essentially monitoring by regular means.

So, let's begin.

Active Directory Monitoring

You can find a lot of articles on AD monitoring on all IT blogging communities, but ... but more than 90% of them are devoted to the use of third-party applications, most of which cost a certain amount of money that not every company is willing to give, even if not a lot. Perhaps the record holder for the number of articles is a product from NetWrix Corporation. Here and there, IT-specialists paint the wonderful features of this program. Yes, what a sin to hide, and he used this program in demo mode. Honestly, I liked it, everything is simple and affordable, but they don’t give money for it, which means that by the end of the demo period AD will again be left without a "keen" eye. What fundamentally did not suit me.

A little toria

As you know, security policies in Windows of all stripes have the ability to audit events. This audit allows you to automatically generate entries in the Event Log in the journal "Security". An audit can be conducted for several types of events, for example: login, access to objects, account management, policy changes, and more. Only 9 types of events. This is a basic audit. Starting from Windows 7 and Windows Server 2008R2, the number of audit events has increased to 53. With the help of this audit, you can audit in more detail only the necessary events. More information on advanced audit policies can be found here .
But as you know, those who at least once looked into the Security section in EventLog find something there - if not impossible, then at least very difficult.


And then an idea was born ... since Windows can create an eventlog entry in EventLog, then theoretically this information can be obtained. One “but” ... this log is too big to search for the desired event manually, and over time, if you do not limit the size of the log, it can grow into tens of gigabytes, which in itself is no longer good. So you need to solve the problem of finding the right information in EventLog automatically. Fortunately, each type of event (for example, creating a user account) has its own ID by which it can be found.
So to solve the search problem, we only need to find this event in the log.
For Powershell 2.0, there is a special cmdlet for working with EventLog - Get-WinEvent .
Using this cmdlet, you can get a specific record in EventLog.


Suppose we indicated in the group policies that apply to domain controllers to audit events related to accounts.
Then any action with an account opened in AD will generate an event that will create an entry in EventLog with a specific identifier. For example, when a computer is added to the domain on the domain controller where this operation was performed, in the EventLog in the Security log there will be an entry with the identifier ID = 4741 , which will indicate at what time, who added which computer to the domain.
To get the last event with the given identifier, we use the Powershell query:

Get-WinEvent -FilterHashtable @{LogName="Security";ID=4741}

But the output format unfortunately wants to leave the best, because a lot of extra information, such as security identifiers, a bunch of attributes.

TimeCreated  : 12.07.2012 14:02:19
ProviderName : Microsoft-Windows-Security-Auditing
Id           : 4741
Message      : Создана учетная запись компьютера.

                   Идентификатор безопасности:        S-1-5-21-451469775-2953165952-2320738315-500
                   Имя учетной записи:        administrator
                   Домен учетной записи:        DOMAIN
                   Идентификатор входа:        0xb3acf

               Новая учетная запись компьютера:
                   Идентификатор безопасности:        S-1-5-21-451469775-2953165952-2320738315-2979
                   Имя учетной записи:        TEST$
                   Домен учетной записи:        DOMAIN

                   Имя учетной записи SAM:    TEST$
                   Отображаемое имя:        -
                   Основное имя пользователя:    -
                   Домашний каталог:        -
                   Домашний диск:        -
                   Путь к сценарию:        -
                   Путь к профилю:        -
                   Рабочие станции пользователя:    -
                   Последний пароль задан:    <никогда>
                   Срок действия учетной записи истекает:        <никогда>
                   Идентификатор основной группы:    515
                   Разрешено делегировать:    -
                   Старое значение UAC:        0x0
                   Новое значение UAC:        0x85
                   Управление учетной записью пользователя:
                       Учетная запись отключена
                       "Пароль не требуется" - включено
                       "Учетная запись доверия рабочей станции" - включено
                   Параметры пользователя:    -
                   Журнал SID:        -
                   Часы входа:        <значение не задано>
                   DNS-имя узла:        -
                   Основные имена служб:    -

               Дополнительные сведения:
                   Privileges        -

We are interested in the most basic information: Time, who created, computer name. To do this, "slightly" tweak our request:
Get-WinEvent -FilterHashtable @{LogName="Security";ID=4741} | Select TimeCreated,@{n="Оператор";e={([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq "SubjectUserName"} |%{$_.'#text'}}},@{n="Имя компьютера";e={([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq "SamAccountName"}| %{$_.'#text'}}}

As a result, the result of this query will be information that is already convenient for the eye:

TimeCreated : 12.07.2012 14:02:19
 Оператор : administrator
 Имя компьютера : TEST$

This request considers the event in EventLog as an XML object. And selects the values ​​we need, i.e. Time (TimeCreated), Operator and Computer Name.

As you can see, the code is not readable. To be able to work with events, the Windows Eventlog provides a special .Net class that can parse each event into substrings, and since Powershell, in fact, is .NET, these capabilities are also available in it.

For example, this code parses an event into substrings:

Get-Eventlog Security -InstanceId 4768|
   Select TimeGenerated,ReplacementStrings |
   % {
     New-Object PSObject -Property @{
     UserName = $_.ReplacementStrings[0]
     IPAddress = $_.ReplacementStrings[9]
     Date = $_.TimeGenerated

As a result, we get something like this:

 Date : 12.07.2012 14:02:19
 Username : administrator
 IPAddress :

This code is much easier to read.

Let's consider in more detail requests.

Option 1 (the request considers the event as XLM):
If you open any entry in the EventLog, you will see 2 bookmarks: General and details.
If you go to the "details" tab and select the viewing mode: "XML mode", then we will see just the same event structure in the form of XML.
After parsing this event as XML and selecting the values ​​we need from it: In the Event.EventData.Data section, in the parameter under the name SubjectUserName is the name of the user who created the computer, and in the parameter under the name SamAccountName - the name of the created computer.

Option 2 (parsing under substrings):

In the same way, open the event as XML, find the section Event.EventData.Data, and count the lines (starting from 0) - these are the indices of our substrings. We find the line with the value we need, and consider what it is in a row.

Now you need to display this information somewhere, do not store it in the console.
And even better if it will be sent to the administrator, say, by mail.
Powershell 2.0 has the ability to console-style SMTP sessions and send emails.
Send-MailMessage - a cmdlet that performs this function.
To send a message, you must specify the SMTP server, sender address, recipient address, message body, message subject, username and password.
As a result, we will receive the following request, which will search for the last event under the identifier ID = 4741 and send information to the administrator by mail.

#Определяем все переменные для отправки
 $Theme = "Добавлен новый компьютер в домен" # Первая строчка в теле письма, чтобы понимать о чем речь.
 $Subject = "Создание компьютера" # Тема сообщения

$Server = "mail.domain.ru" # SMTP Сервер
 $From = "audit@domain.ru" # Адрес отправителя
 $To = "admin@domain.ru" # Получатель
 $pass = ConvertTo-SecureString "PASSWORD" -AsPlainText -Force #Пароль от учетной записи
 $cred = New-Object System.Management.Automation.PSCredential("AUDIT" , $pass) #Имя пользователя и пароль
 $encoding = [System.Text.Encoding]::UTF8 #Устанавливаем кодировку UTF8 для корректного отображения информации в теле письма

#Собственно сам запрос поиска события. Выбирается последнее произошедшее событие с таким ID. Данные записываются в переменную Body.

$Body=Get-WinEvent -FilterHashtable @{LogName="Security";ID=4741} | Select TimeCreated,@{n="Оператор";e={([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq "SubjectUserName"} |%{$_.'#text'}}},@{n="Имя компьютера";e={([xml]$_.ToXml()).Event.EventData.Data | ? {$_.Name -eq "SamAccountName"}| %{$_.'#text'}}} | select-object -first 1

#Отправка письма.
 Send-MailMessage -From $From -To $To -SmtpServer $server -Body "$Theme `n$BodyM" -Subject $Subject -Credential $cred -Encoding $encoding


We save this script into a file with the ps1 extension, for example, here: D: \ Scripts \ ADCompAdd.ps1
Open the Powershell console.
We type the command: Set-ExecutionPolicy Unrestricted
Press "Y" and Enter. Thus, we allow the execution of Powershell scripts on the server.
Drag the script to the console (Drag and Drop) and press Enter. We check that the script was executed without errors (i.e., no red text boxes appeared in the console). We check the mail for a new message that contains the data we need.

It remains only to somehow make this script run at the moment when the event occurred.
Then the "Task Scheduler" will come to our aid.
The scheduler has the ability to respond to a specific event in EventLog.
We create a task where in the trigger we indicate to respond to the event under the number 4741 that appears in the Security log.
We also indicate that you need to run this script. To do this, indicate in the "actions" that we want to run the program, in the "Program or script" field, write " powershell ". In the field "Add arguments (optional)" write " -nologo -noprofile -File" D: \ Scripts \ ADCompAdd.ps1 ″ "
Now we test how the created structure works. We create a test computer in any unit in AD. And check the mail for a message .

The script is not completely safe because It contains a user name and password in clear text, so I highly recommend that if you decide to use this script, then use accounts to send messages with a minimum set of rights.

According to my measurements, the reaction time to the event is 1 second. Those. 1 second elapses from the time of creation to receipt of the letter. Of course, provided that you use your local mail server, and not somewhere on the Internet. There the delay may be longer. But overall not high either.

As a result, taking this script as a basis and changing the event number and data that must be obtained from the event in it, you can monitor all operations with accounts in AD: create-delete, disable-enable, lock-unlock., Add to groups and exceptions and stuff. In general, any event monitoring that allows you to audit Windows. It is just necessary to change the XML filter in the request, to do this, look at the required event in XML mode, select the necessary values ​​and enter them into the request filter.


Here are some useful event identifiers for Windows Server 2008R2:
ID = 4741 Creating a computer in the domain
ID = 4743 Removing a computer from the domain
ID = 4728 Adding a security group
ID = 4729 Removing from the security group
ID = 4720 Creating a user
ID = 4726 Removing user
ID = 4740 Lock account
ID = 4767 Unlock account
ID = 4722 Enable account
ID = 4725 Disable account