Предыстория
Началась вся история с того, что подошло время очередного IT аудита. Пришли серьезные дяденьки из Price Waterhouse Coopers, дали нам массу указаний и пару скриптов, которые надо было запустить на контроллере домена чтобы потом выслать им логи. После ознакомления с текстами скриптов (а мало-ли что там, безопасность превыше всего) логи были им предоставлены. И тут началось.
Требования PWC в большинстве случаев касались политик безопасности. Одно из них было — ввести password complexity policy и pasword lifetime. Сделать это, естественно, было довольно легко, но вскоре мы столкнулись с вытекающей проблемой: Windows не уведомляет пользователя об истечении срока действия его пароля если подключение осуществляется через VPN из внешней сети. Проблема оказалась довольно серьезной потому, что просто обновить пароль и разблокировать аккаунт такого пользователя было уже недостаточно. Нужно было чтобы ноутбук оказался в родной, офисной сети. С учетом того что некоторые пользователи «живут» в вечных командировках — проблема оказалась очень серьезной. Вручную отслеживать их — та еще головная боль, да и не по админски как-то. Тут и возникла мысль организовать рассылку автоматических уведомлений. После непродолжительного времени, потраченного на поиски, был найден неплохой скрипт, который отдаленно пытался выполнять нужные действия. Мне предстояло его доработать.
Так как это был мой первый опыт написания скрипта под PowerShell — времени было потрачено немало (почти полный рабочий день).
Скрипт
И вот что у меня получилось:
Import-Module ActiveDirectory #System globalization #$ci = New-Object System.Globalization.CultureInfo("ru-RU") #SMTP server name $smtpServer = "mail.domain.local" #Creating a Mail object $msg = new-object Net.Mail.MailMessage #Creating a Mail object for report $msgr = new-object Net.Mail.MailMessage #Creating SMTP server object $smtp = new-object Net.Mail.SmtpClient($smtpServer) #E-mail structure Function EmailStructure($to,$expiryDate,$upn) { $msg.IsBodyHtml = $true $msg.From = "notification@domain.com" $msg.To.Clear() $msg.To.Add($to) $msg.Subject = "Password expiration notice" $msg.Body = "<html><body><font face='Arial'>This is an automatically generated message from Exchange ervice.<br><br><b>Please note that the password for your account <i><u>Domain$upn</u></i> will expire on $expiryDate.</b><br><br>Please change your password immediately or at least before this date as you will be unable to access the service without contacting your administrator.</font></body></html>" } Function EmailStructureReport($to) { $msgr.IsBodyHtml = $true $msgr.From = "notification@domain.com" $msgr.To.Add($to) $msgr.Subject = "Script running report" $msgr.Body = "<html><body><font face='Arial'><b>This is a daily report.<br><br>Script has successfully completed its work.<br>$NotificationCounter users have recieved notifications:<br><br>$ListOfAccounts<br><br></b></font></body></html>" } #Set the target OU that will be searched for user accounts $OU = "OU=Organisation,DC=domain,DC=local" $ADAccounts = Get-ADUser -LDAPFilter "(objectClass=user)" -searchbase $OU -properties PasswordExpired, extensionAttribute15, PasswordNeverExpires, PasswordLastSet, Mail, Enabled | Where-object {$_.Enabled -eq $true -and $_.PasswordNeverExpires -eq $false} $NotificationCounter = 0 $ListOfAccounts = "" Foreach ($ADAccount in $ADAccounts) { $accountFGPP = Get-ADUserResultantPasswordPolicy $ADAccount if ($accountFGPP -ne $null) { $maxPasswordAgeTimeSpan = $accountFGPP.MaxPasswordAge } else { $maxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge } #Fill in the user variables $samAccountName = $ADAccount.samAccountName $userEmailAddress = $ADAccount.ExtensionAttribute15 $userPrincipalName = $ADAccount.UserPrincipalName if ($ADAccount.PasswordExpired) { Write-host "The password for account $samAccountName has expired!" } else { $ExpiryDate = $ADAccount.PasswordLastSet + $maxPasswordAgeTimeSpan $TodaysDate = Get-Date $DaysToExpire = $ExpiryDate - $TodaysDate #Calculating DaysToExpireDD to DD format (w/o fractional part and dot) $DaysToExpireDD = $DaysToExpire.ToString() -Split ("S{17}$") Write-host "The password for account $samAccountName expires on: $ExpiryDate. Days left: $DaysToExpireDD" if (($DaysToExpire.Days -eq 15) -or ($DaysToExpire.Days -eq 7) -or ($DaysToExpire.Days -le 3)) { $expiryDate = $expiryDate.ToString("d",$ci) #Generate e-mail structure and send message if ($userEmailAddress) { EmailStructure $userEmailAddress $expiryDate $samAccountName $smtp.Send($msg) Write-Host "NOTIFICATION - $samAccountName :: e-mail was sent to $userEmailAddress" $NotificationCounter = $NotificationCounter + 1 $ListOfAccounts = $ListOfAccounts + $samAccountName + " - $DaysToExpireDD days left. Sent to $userEmailAddress<br>" } } } } Write-Host "SENDING REPORT TO IT DEPARTMENT" EmailStructureReport("itdepartment@domain.com") $smtp.Send($msgr)
Сохраняем это как текст в файл с расширением .ps1.
Команда запуска
Далее — создаем файл с расширением .cmd и пишем в него параметр запуска скрипта. У меня это выглядит так:
powershell D:ExchangeToolspwde.ps1
Оба файла у меня лежат на почтовом сервере. Вы можете попробовать свой вариант.
Создание графика выполнения
Далее ставим в расписание ежедневный запуск .cmd файла. У меня он выполняется каждый день в 11 утра.
Start > All programs > Accesories > System tools > Task scheduler.
Жмем Action > Create new task.
На вкладке General жмем кнопку «Change user» выбираем пользователя, от имени которого всё это будет работать. У пользователя должны быть права на считывание параметров, необходимых для скрипта из AD. Ставим «Run whether user logged on or not» (запускать независимо от того, залогинен пользователь или нет). При сохранении задачи система попросит вас ввести пароль пользователя.
Далее вкладка Triggers. Тут всё просто — настраиваете время запуска как вам нужно.
Вкладка Actions, жмем «New», выбираем «Start a program» и указываем путь к нашему .cmd файлу. Последние две вкладки можно не трогать, но если есть необходимость — внесите изменения как считаете нужным.
Уведомления посылаются за 15, 7 и 3 и менее дней.
ВНИМАНИЕ
Exchange серверу нужно указать собственный адрес в качестве релея если планируется отправка на адреса, находящиеся вне домена (дублирование на личный адрес, например).
Некоторые, вероятно, спросят — «зачем же брать адрес из ExtensionAttribute если есть стандартное поле содержащее e-mail пользователя?». Ответ прост — копия каждого уведомления шлется также в IT department, у пользователя может быть не один адрес и некоторые системные аккаунты не имеют почтовых ящиков в принципе, но уведомления о них нужны для облегчения жизни уже непосредственно администраторам. Вписать адреса в ExtensionAttribute15 вы сможете зайдя в дерево Active Directory на контроллере домена с правами администратора. В свойствах аккаунта будет вкладка «Attribute Editor». Если адресов в поле будет несколько — разделять их следует запятой.
Автор: Pascal_tgn