Автоматическая проверка изменения локальной группы с уведомлением на почту

Обновлено 02.06.2024

Отправка писем через powershell

Добрый день! Уважаемые читатели и гости  IT блога Pyatilistnik. В предыдущей статье мы разобрали перезапуск службы печати с различными инструментами для выполнения данной задачи. Сегодня я хочу с вами поделиться полезным сценарием, который позволит вам настроить простой мониторинг локальной группы администраторы, в случае когда будет изменено ее членство. В результате вы получите письмо с таблицей, где будет отображено данное изменение. Такие уведомления я настраиваю на важных серверах. где кроме системных администраторов могут быть еще и другие сотрудники.

Постановка задачи по мониторингу состава локальной группы безопасности

И так у меня есть ферма ADFS серверов. ADFS (Active Directory Federation Services) — это служба одной аутентификации, разработанная компанией Microsoft, которая позволяет пользователям использовать одни учетные данные для доступа к различным приложениям в пределах организации или между организациями. Основное предназначение ADFS — обеспечить безопасную и удобную федерацию идентичности с помощью учетной записи Active DIrectory.

Основные функции ADFS включают:

  1. Единая аутентификация: Пользователи могут войти в систему один раз и получить доступ ко всем приложениям, которые поддерживают федерацию идентичности через ADFS, без необходимости повторного ввода учетных данных.
  2. Федерация идентичности: ADFS позволяет организациям установить доверительные отношения между собой, чтобы пользователи могли использовать свои учетные данные для доступа к ресурсам в различных организациях.
  3. Безопасность: ADFS обеспечивает механизмы безопасности, такие как шифрование и проверка подлинности, чтобы гарантировать безопасность передачи данных и защитить систему от несанкционированного доступа.
  4. Интеграция с Active Directory: ADFS интегрируется с существующей инфраструктурой Active Directory, что упрощает процесс внедрения и управления службой.

Так как сервис ADFS могут использовать не только системные администраторы, а коллеги из разработки, то у такого важного сервиса должен быть мониторинг по правам на него. Самое простое, что вы можете сделать, это взять PowerShell скрипт, который я приведу ниже, вы можете его доработать под себя.

Задача, простая получить текущий состав группы Администраторы на каждый ноде ADFS кластера, сохранить его как эталонный, если происходит изменение состава группы безопасности, то сравнить его с эталонным, выявить отличия и выслать это письмом в виде таблицы.

#Объявляет функцию Date, которая использует Get-Date для получения текущей даты и времени в формате "yyyy.MM.dd HH:mm:ss

function Date {Get-Date -Format "yyyy.MM.dd HH:mm:ss"}

#Создает переменную $log_folder, которая представляет путь к папке журналов (Logs) в той же директории, где находится скрипт, и добавляет к нему имя скрипта без расширения ".ps1".

$log_folder = "$PSScriptRoot\Logs\" + $($MyInvocation.MyCommand.Name -replace (".ps1", ""))

#Создает переменную $log, которая представляет путь к файлу журнала, используя текущую дату и время в формате "yyyy_MM_dd_HH_mm_ss" в имени файла.
$log = "$log_folder\$(Get-Date -Format "yyyy_MM_dd_HH_mm_ss").txt"

#Проверяет существование папки $log_folder с помощью Test-Path. Если папка не существует, то создает ее с помощью New-Item, указывая тип элемента (D для директории) и флаг Force для принудительного создания.

if (! (Test-Path $log_folder -ErrorAction SilentlyContinue))
{
New-Item $log_folder -ItemType D -Force
}

# Определяет переменные $local_admins_last_file и $local_admins_prev_file, представляющие пути к файлам local_admins_last.csv и local_admins_prev.csv в папке $log_folder.

$local_admins_last_file = "$log_folder\local_admins_last.csv"
$local_admins_prev_file = "$log_folder\local_admins_prev.csv"

#Создает массив $ADFS_servers, содержащий имена серверов ADFS.

$ADFS_servers = @(
"ADFS01.root.pyatilistnik.org"
"ADFS02.root.pyatilistnik.org"
"ADFS03.root.pyatilistnik.org"
)

#Записывает в журнал (лог-файл) текущую дату и время с сообщением "Start Processing".

"$(Date) Start Processing" | Tee-Object $log -Append

#Инициализирует пустой массив $out_data, который будет использоваться для хранения данных о локальных администраторах.

$out_data = @()

#Запускает цикл foreach, перебирающий каждый сервер из массива $ADFS_servers в отсортированном порядке.

foreach ($server in $ADFS_servers | sort)
{
"$(Date) Analyzing local administrators for $server" | Tee-Object $log -Append

$administrators = $null

# Записывается в журнал сообщение, что происходит анализ локальных администраторов для конкретного сервера. Пытается получить список локальных администраторов на сервере, используя команду Invoke-Command. В случае возникновения ошибки при выполнении команды, записывает сообщение об ошибке в журнал.

try {
$administrators = Invoke-Command -ScriptBlock{Get-LocalGroupMember -Name "Administrators"} -ComputerName $server -ErrorAction Stop
}
catch {
"$(Date) $($_.exception.message)" | Tee-Object $log -Append
}

#Для каждого найденного администратора добавляет новый объект в массив $out_data с данными о сервере и локальном администраторе.

foreach ($administrator in $administrators)
{
$out_data += New-Object PSObject –Prop @{
"Server" = $administrator.PSComputerName
"Local Administrator" = $administrator.Name
}
}
}

#Создает строку, в которой используется переменная $(Date) для вставки текущей даты и времени. Эта строка содержит сообщение, что происходит попытка экспорта данных о локальных администраторах в файл $local_admins_last_file. Затем это сообщение выводится на экран и дополнительно записывается в лог-файл с помощью команды Tee-Object с параметром -Append, что позволяет добавлять данные в конец файла без перезаписи.

"$(Date) Trying to export local administrators data to $local_admins_last_file file" | Tee-Object $log -Append

#Далее, массив данных $out_data экспортируется в CSV-файл $local_admins_last_file с использованием команды Export-Csv. Опции -Encoding Unicode, -NoTypeInformation и -Force задают кодировку файла (Unicode), отключают добавление информации о типах данных в CSV-файл и принудительно перезаписывают файл, если он уже существует.

$out_data | Export-Csv $local_admins_last_file -Encoding Unicode -NoTypeInformation -Force

#Строка создает сообщение о попытке сравнения файлов $local_admins_last_file и $local_admins_prev_file с добавлением текущей даты и времени. Это сообщение выводится на экран и записывается в лог-файл.

"$(Date) Trying to compare $local_admins_last_file and $local_admins_prev_file files " | Tee-Object $log -Append

#Создается пустой массив $out_data, который будет содержать результаты сравнения.

$out_data = @()

#С помощью цикла foreach перебираются серверы из массива $ADFS_servers, отсортированные по порядку.

foreach ($server in $ADFS_servers | sort) # | select -First 1

#Для каждого сервера извлекаются данные о членах группы локальных администраторов из предыдущего и текущего файлов CSV ($local_admins_last_file и $local_admins_prev_file) и сохраняются в массивы $administrators_last и $administrators_prev.
{
$administrators_last = $administrators_prev = @()

$administrators_last = (Import-Csv $local_admins_last_file | ? {$_.Server -eq $server})."Local Administrator"
$administrators_prev = (Import-Csv $local_admins_prev_file | ? {$_.Server -eq $server})."Local Administrator"

# Выполняется сравнение массивов, чтобы найти различия между членами группы на предыдущем и текущем серверах. Результат сохраняется в переменную $diff.

$diff = $null
$diff = (@($administrators_last | ? {$administrators_prev -NotContains $_}) + @($administrators_prev | ? {$administrators_last -NotContains $_})) -join ", "

# Если есть различия (длина строки $diff больше нуля), создается новый объект PowerShell с данными о сервере и различиях в членах группы локальных администраторов. Этот объект добавляется в массив $out_data.

if ($diff.Length -eq 0) {Continue}

$out_data += New-Object PSObject –Prop @{
"Server" = $server
"Local Administrators group members differences" = $diff
}
}

#Производим подключение к почтовому серверу и формируем таблицу

"$(Date) Found $(($out_data | Measure-Object).Count) differences" | Tee-Object $log -Append

if (($out_data | Measure-Object).Count -gt 0)
{
$from = "tech@mail.pyatilistnik.org"
$to = "admin@pyatilistnik.org" #

$Subject = "Check new ADFS Administrators"
$smtpserver = "mail.pyatilistnik.org"
$secure = $true
$port = 587 # TLS, for SSL use 465
$username = "tech"
$password = 'P@ssw0rd'

$Header = @"
<style>
TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}
TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
</style>
"@
$body = $null
$body += "<p>Check new ADFS Administrators:"

$body += $out_data | sort Server | select Server, "Local Administrators group members differences" | ConvertTo-Html -Head $Header
$body += "<p>Generated on $($env:COMPUTERNAME + "." + $((Get-WmiObject Win32_ComputerSystem).Domain))"

###

$message = New-Object System.Net.Mail.MailMessage
$message.From = $from
$message.To.Add($to)
$message.Subject = $Subject
$message.Body = $body
$message.IsBodyHtml = $true

$smtp = New-Object Net.Mail.SmtpClient($smtpserver, $port)
$smtp.EnableSsl = $secure
$smtp.Credentials = New-Object System.Net.NetworkCredential($username, $password)

"$(Date) Trying to send e-mail notification" | Tee-Object $log -Append

try {
$smtp.Send($message)
}
catch {
"$(Date) $($_.exception.message)" | Tee-Object $log -Append
}
}
else
{
"$(Date) Nothing to send" | Tee-Object $log -Append
}
#Выводит сообщение о попытке переименовать файлы $local_admins_last_file в $local_admins_prev_file с добавлением текущей даты и времени. Это сообщение записывается как лог и выводится на экран.

"$(Date) Trying to rename $local_admins_last_file to $local_admins_prev_file files " | Tee-Object $log -Append

# Затем идет блок try, который пытается выполнить следующие действия: Сначала удаляется файл $local_admins_prev_file с применением параметра -Force, чтобы удаление происходило без запроса подтверждения. Затем файл $local_admins_last_file переименовывается в $local_admins_prev_file с применением параметра -Force.

try {
Remove-Item $local_admins_prev_file -Force -ErrorAction Stop
Rename-Item -Path $local_admins_last_file -NewName $local_admins_prev_file -Force -ErrorAction Stop
}

#Если в блоке try возникает исключение (ошибка), то программа переходит в блок catch, где записывается сообщение об ошибке в лог-файл, включая текущую дату и текст ошибки.
catch {
"$(Date) $($_.exception.message)" | Tee-Object $log -Append
}

###

"$(Date) End Processing" | Tee-Object $log -Append

В результате вы получите вот такое аписьмо с таблицой в которой будут отображены изменения в локальной группе безопасности на пуле серверов.

Check new ADFS Administrators

Надеюсь, что вы взяли скрипт себе на вооружение, остается его добавить в планировщик задач и использовать. На этом у меня всё, с вами был Иван Сёмин, автор и создатель IT портала Pyatilistnik.org.

Автор - Сёмин Иван

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *